Как я могу централизовать валидацию modelstate в asp.net mvc с помощью фильтров действий?

Я пишу этот код в нескольких местах и ​​всегда повторяю эту логику:

public ActionResult MyMethod(MyModel collection)
{
    if (!ModelState.IsValid)
    {
        return Json(false);//to read it from javascript, it always equal
    }
    else
    {
        try
        {
            //logic here
            return Json(true);//or Json(false);
        }
        catch
        {
            return Json(false);//to read it from javascript, it always equal
        }
    }
}

Есть ли способ использования фильтров действий, чтобы не повторять try-catch, спросить, действительна ли модель и return Json(false) как ActionResult?

Ответы

Ответ 1

Чтобы соответствовать REST, вы должны вернуть http bad request 400, чтобы указать, что неверный запрос (модель недопустима) вместо возврата Json(false).

Попробуйте этот атрибут из официальный сайт asp.net для веб-api:

public class ValidateModelAttribute : ActionFilterAttribute
{
     public override void OnActionExecuting(HttpActionContext actionContext)
     {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Response = actionContext.Request.CreateErrorResponse(
                HttpStatusCode.BadRequest, actionContext.ModelState);
        }
    }
}

Версия для asp.net mvc может быть такой:

public class ValidateModelAttribute : ActionFilterAttribute
{
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
              if (!filterContext.Controller.ViewData.ModelState.IsValid)
              {
                   filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.BadRequest);  
              }
        }
}

Ответ 2

Если вы хотите сделать это в MVC6 или Mvc Core и не указав свой атрибут во всех своих методах действий, тогда вы это сделаете.

Сначала создайте ActionFilter

public class ModelStateValidationFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting( ActionExecutingContext context )
    {
        if ( context.HttpContext.Request.Method == "POST" && !context.ModelState.IsValid )
            context.Result = new BadRequestObjectResult( context.ModelState );
    }
}

Теперь создайте соглашение, в котором вы будете применять этот ActionFilter ко всем вашим контроллерам.

    public class ModelStateValidatorConvension : IApplicationModelConvention
    {
        public void Apply( ApplicationModel application )
        {
            foreach ( var controllerModel in application.Controllers )
            {
                controllerModel.Filters.Add( new ModelStateValidationFilterAttribute() );
            }
        }
    }

И последнее, что нужно зарегистрировать это соглашение в MVC

    public void ConfigureServices( IServiceCollection services )
    {
      services.Configure<MvcOptions>( x => x.Conventions.Add( new ModelStateValidatorConvension() ) );
    }

Ответ 3

Вот как использовать код от Khanh TO (от официальный сайт asp.net):

Чтобы применить этот фильтр ко всем контроллерам веб-API, добавьте экземпляр фильтра в коллекцию HttpConfiguration.Filters во время конфигурации:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new ValidateModelAttribute());

        // ...
    }
}

Другой вариант - установить фильтр как атрибут для отдельных контроллеров или действий контроллера:

public class ProductsController : ApiController
{
  [ValidateModel]
  public HttpResponseMessage Post(Product product)
  {
    // ...
  }
}