ASP.NET Detailed explanation of core implementation of custom webapi model validation

Time:2020-6-9

Framework Era 

In the framework era, when we generally conduct parameter verification, the following code is very common


[HttpPost]
  public async Task<JsonResult> SaveNewCustomerAsnyc(AddCustomerInput input)
  {
   if (!ModelState.IsValid)
   {
    return Json(Result.FromCode(ResultCode.InvalidParams));
   }

   .....
  }

Or the advanced point is to implement iactionfilter to intercept, as follows:

public class ApiValidationFilter : IActionFilter
 {
  public bool AllowMultiple => false;

  public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
  {
   var method = actionContext.ActionDescriptor.GetMethodInfoOrNull();
   if (method == null)
   {
    return await continuation();
   }   

   if (!actionContext.ModelState.IsValid)
   {
    var error = actionContext.ModelState.GetValidationSummary();
    var result =  Result.FromError ($"parameter validation failed: {error}", ResultCode.InvalidParams );
    return actionContext.Request.CreateResponse(result);
   }

   return await continuation();
  }
}
public static class ModelStateExtensions
 {
  /// <summary>
  ///Get the validation message prompt and format the prompt
  /// </summary>
  public static string GetValidationSummary(this ModelStateDictionary modelState, string separator = "\r\n")
  {
   if (modelState.IsValid) return null;

   var error = new StringBuilder();

   foreach (var item in modelState)
   {
    var state = item.Value;
    var message = state.Errors.FirstOrDefault(p => !string.IsNullOrWhiteSpace(p.ErrorMessage))?.ErrorMessage;
    if (string.IsNullOrWhiteSpace(message))
    {
     message = state.Errors.FirstOrDefault(o => o.Exception != null)?.Exception.Message;
    }
    if (string.IsNullOrWhiteSpace(message)) continue;

    if (error.Length > 0)
    {
     error.Append(separator);
    }

    error.Append(message);
   }

   return error.ToString();
  }
 }

Then register the interceptor in the boot entry for use

. net core Era

Automatic model state verification

In the era of. Net core, the framework will help you automatically verify the state of the model, that is, the model state. The framework will automatically register the modelstateinvalidfilter for you, which will run in the onactionexecuting event.

If the code is written based on the existing framework, we no longer need to couple such model judgment code in the business. The system will check whether the modelstate is valid or not. If it is invalid, it will directly return 400 badrequest, so there is no need to execute the following code to improve efficiency. Therefore, the following code is no longer required in the operation method:


if (!ModelState.IsValid)
{
  return BadRequest(ModelState);
}

Problem introduction

In our real development, when we encounter a parameter validation failure 400 error, we want to return an understandable JSON result in the background instead of directly returning 400 error in the page. So we need to replace the default badrequest response result and replace the result with the JSON result we want to return.

Custom badrequest response

How do we change ASP.NET What is the default behavior of core web API model validation? The specific method is to configure apibehaviorioptions through the configureservices method of startup. Let’s take a look at this class first.


public class ApiBehaviorOptions
  {
    public Func<ActionContext, IActionResult> InvalidModelStateResponseFactory { get; set; }

    public bool SuppressModelStateInvalidFilter { get; set; }

    public bool SuppressInferBindingSourcesForParameters { get; set; }

    public bool SuppressConsumesConstraintForFormFileParameters { get; set; }
  }

The default property of all bool types is false.

Scheme I

Default behavior is disabled when the suppressmodelstateinvalidfilter property is set to true

public void ConfigureServices(IServiceCollection services)
    {      
      services
         .AddMvc()
        . addxmlserializerformatters() // set to support XML format input and output
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

      //Disable default behavior
      services.Configure<ApiBehaviorOptions>(options =>
      {
        options.SuppressModelStateInvalidFilter = true;
      });
    }

After we disable it, we need to customize the return results. We use the API validation filter defined above to intercept and return. You need to register this interceptor in the configureservices method

public void ConfigureServices(IServiceCollection services)
    {
      .....
      services
         .AddMvc(options =>
         {
           options.Filters.Add<ApiValidationFilter>();
         })
        . addxmlserializerformatters() // set to support XML format input and output
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    }

Scheme II

This is also the recommended practice on the official website. To customize the response caused by validation error, please use invalidmodelstateresponsefactory. This invalidmodelstateresponsefactory is a delegate whose parameter is actioncontext and return value is iactionresult. The specific implementation is as follows:

public void ConfigureServices(IServiceCollection services)
    {      
      services
         .AddMvc()
        . addxmlserializerformatters() // set to support XML format input and output
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

      //Parameter validation
      services.Configure<ApiBehaviorOptions>(options =>
      {
        options.InvalidModelStateResponseFactory = (context) =>
        {
          var error = context.ModelState.GetValidationSummary();
          
          return new JsonResult( Result.FromError ($"parameter validation failed:{ error.ToString ()}",  ResultCode.InvalidParams );
        };
      });
    }

The above code is to override the default behavior of model state management (API behavior options). When the data model validation fails, the program will execute this code. If the model state fails to pass the validation, the error information it throws will be returned to the client by using jsonresult through formatting.

summary

In the actual application process, for the development of webapi, basically all requests need to return custom results, so we need to override the default override default model authentication behavior. The above two schemes are given:

The first scheme: conforms to the style of the framework era, and needs to additionally override the original model validation (suppressmodelstateinvalidfilter = true)

The second scheme: the official recommended practice is in line with the core era style. You only need to copy the invalidmodelstateresponsefactory delegation, and individuals also recommend the second scheme.

Well, that’s all the content of this article. I hope that the content of this article has a certain reference learning value for your study or work. Thank you for your support for developpaer.

Recommended Today

Review of SQL Sever basic command

catalogue preface Installation of virtual machine Commands and operations Basic command syntax Case sensitive SQL keyword and function name Column and Index Names alias Too long to see? Space Database connection Connection of SSMS Connection of command line Database operation establish delete constraint integrity constraint Common constraints NOT NULL UNIQUE PRIMARY KEY FOREIGN KEY DEFAULT […]