В большинстве примеров проверка входных данных ASP.NET MVC осуществляется следующим образом:
Этот код можно улучшить:
Авторизация в ASP.NET MVC настраивается с помощью атрибутов. Сделаем по аналогии и объявим атрибут для валидации:
На stackoverflow обсуждался вопрос «какой код возвращать при ошибке валидации». Семейство 4** выглядит наиболее подходящим. 422 — уже используется Ruby из коробки. ASP.NET MVC не предлагает best practice на этот счет. Не вижу причин не привести в соответствие с Ruby:
Остается только разделить контроллеры, возвращающие
[HttpPost]
public IActionResult Test(SomeParam param)
{
if (!ModelState.IsValid)
{
return View(param);
// return Json({success: false, state: ModelState});
}
dbContext.UpdateData(param);
return RedirectToAction("index");
// return Ok({success: true});
}
Этот код можно улучшить:
- вынести валидацию из тела метода и избавиться от дублирования
if (!ModelState.IsValid)
- вернуть код ответа 422
Вынесем валидацию в ActionFilter
Авторизация в ASP.NET MVC настраивается с помощью атрибутов. Сделаем по аналогии и объявим атрибут для валидации:
public enum ValidationResult
{
View,
Json
}
public class ValidationFilterAttribute: ActionFilterAttribute
{
private readonly ValidationResult _result;
public ValidationFilterAttribute(ValidationResult result
= ValidationResult.Json)
{
_result = result;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
if (_result == ValidationResult.Json)
{
context.Result = new ValidationFailedResult(context.ModelState);
}
else
{
context.Result = ((Controller)context.Controller).View(
context.ActionArguments.Values.First());
ValidationFailedResult.SetStatusCodeAndHeaders(
context.HttpContext);
}
}
}
}
Добавим код ответа сервера и дополнительную информацию
На stackoverflow обсуждался вопрос «какой код возвращать при ошибке валидации». Семейство 4** выглядит наиболее подходящим. 422 — уже используется Ruby из коробки. ASP.NET MVC не предлагает best practice на этот счет. Не вижу причин не привести в соответствие с Ruby:
internal class ValidationFailedResult: JsonResult
{
public ValidationFailedResult(ModelStateDictionary modelState)
: base(modelState.Select(x => new
{
x.Key,
ValidationState = x.Value.ValidationState.ToString(),
x.Value.Errors
}).ToList())
{
}
public override void ExecuteResult(ActionContext context)
{
base.ExecuteResult(context);
SetStatusCodeAndHeaders(context.HttpContext);
}
internal static void SetStatusCodeAndHeaders(HttpContext context)
{
context.Response.StatusCode = 422;
context.Response.Headers.Add("X-Status-Reason", "Validation failed");
}
}
Используем атрибут
ValidationFilterAttribute
можно использовать на - методе контроллера
- контроллере
- глобально
Остается только разделить контроллеры, возвращающие
View
от Json
. Этого можно добиться, создав два базовых класса или добавив соглашение в атрибут, например проверять наличие api в namespace контроллера.Примеры кода приведены для ASP.NET MVC Core. Для ASP.NET MVC придется создать два набора атрибутов для пространства именMvc
иHttp
, соответственно.