В большинстве примеров проверка входных данных 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, соответственно.
