📜 ⬆️ ⬇️

Getting rid of the boilerplate for validation in ASP.NET MVC

In most examples , ASP.NET MVC input data validation is as follows:

[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}); } 

This code can be improved:

  1. remove validation from the method body and get rid of duplication if (!ModelState.IsValid)
  2. return answer code 422

We will issue validation to ActionFilter


Authorization in ASP.NET MVC is configured using attributes. We make by analogy and declare an attribute for validation:
')
 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); } } } } 

Add server response code and additional information


On stackoverflow , the question “what code to return upon validation error” was discussed. Family 4 ** looks the most appropriate. 422 - Ruby is already used out of the box. ASP.NET MVC does not offer best practice on this. I see no reason not to align with 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"); } } 

Use attribute


ValidationFilterAttribute can be used on

  1. controller method
  2. the controller
  3. globally

It remains only to separate the controllers that return the View from Json . This can be achieved by creating two base classes or adding an agreement to an attribute, for example, to check for the presence of api in the namespace of the controller.

Code examples are given for ASP.NET MVC Core. For ASP.NET MVC, you will have to create two attribute sets for the Mvc and Http namespace, respectively.

Source: https://habr.com/ru/post/346308/


All Articles