【问题标题】:Web API Validation not trigger with custom model binderWeb API 验证不使用自定义模型绑定器触发
【发布时间】:2017-04-19 12:35:44
【问题描述】:

我正在使用 Web API 5 构建 Web 服务。我正在通过扩展 IModelBinder 接口以将复杂类型作为参数映射到操作来实现自定义模型绑定器。绑定部分工作正常。但是模型验证不会发生。 ModelState.IsValid 始终为真。

public class PagingParamsVM
{
        [Range(1, Int32.MaxValue, ErrorMessage = "Page must be at least 1")]
        public int? Page { get; set; }

        [Range(1, Int32.MaxValue, ErrorMessage = "Page size must be at least 1")]
        public int? PageSize { get; set; }
}

public class PaginationModelBinder : IModelBinder
{
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
              var model = (PagingParamsVM)bindingContext.Model ?? new PagingParamsVM();
              //model population logic
              .....

              bindingContext.Model = model;
              return true;
        }
}

public IEnumerable<NewsItemVM> Get([ModelBinder(typeof(PaginationModelBinder))]PagingParamsVM pegination)
{
            //Validate(pegination); //if I call this explicitly ModelState.IsValid is set correctly.
            var valid = ModelState.IsValid; //this is always true
}

public class ModelStateValidationActionFilter : ActionFilterAttribute
{
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var valid = actionContext.ModelState.IsValid //this is always true.
        }
}

如果我显式调用 Validate() 或使用 [FromUri] 属性,则 ModelState.IsValid 设置正确。

public IEnumerable<NewsItemVM> Get([FromUri]PagingParamsVM pegination)
{
            var valid = ModelState.IsValid;
}

我应该在模型绑定器中实现验证部分吗?如果是这样我应该如何实施?

【问题讨论】:

  • SO answer 可能重复。
  • @MihailStancescu 我看到了这个问题。它适用于 DataAnnotations。但如果我使用 FluentValidation 或类似的,它就行不通了。所以这里似乎缺少一些东西。

标签: c# asp.net asp.net-web-api asp.net-web-api2


【解决方案1】:

DefaultModelBinder.CreateModel 应该可以帮助您保持模型状态:

public class PaginationModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        if(modelType == typeof(PagingParamsVM))
        {
            var page = default(int?);
            var model = bindingContext.Model;
            var valueProvider = bindingContext.ValueProvider;
            var pageValue = valueProvider.GetValue("Page");
            var tmp = default(int);
            if(pageValue != null && int.TryParse(pageValue.AttemptedValue, out tmp))
            {
                page = tmp;
            }

            var pageSize = default(int?);
            var sizeValue = valueProvider.GetValue("PageSize");
            if(sizeValue != null && int.TryParse(sizeValue.AttemptedValue, out tmp))
            {
                pageSize = tmp;
            }
            return new PagingParamsVM { Page = page, PageSize = pageSize };
        }
        return base.CreateModel(controllerContext, bindingContext, modelType);
    }
}

使用 binder 的 web api 控制器可以是:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;

public class NewsItemController : ApiController
{
    public IEnumerable<NewsItemVM> Get([ModelBinder(typeof(PaginationModelBinder))]PagingParamsVM pegination)
    {
        //Validate(pegination); //if I call this explicitly ModelState.IsValid is set correctly.
        var valid = ModelState.IsValid; //this is always true
        return Enumerable.Empty<NewsItemVM>();
    }
}

【讨论】:

  • Web API 没有 DefaultModelBinder。 DefaultModelBinder 带有 MVC。
  • @sajith,当您在 MVC .NET 应用程序中开发 Web API 时,DefaultModelBinder 将会出现:)
  • 是的,我知道。但问题是你不能在控制器扩展 ApiController 中使用你的 PaginationModelBinder : DefaultModelBinder。
  • 你试过你的代码了吗?我之前试过这个。它给出了以下错误,无法从“PaginationModelBinder”创建“IModelBinder”。请确保它派生自“IModelBinder”并具有公共无参数构造函数。我认为原因是模型绑定器必须从 System.Web.Http.ModelBinding.IModelBinder 派生。但是 DefaultModelBinder 在 System.Web.Mvc 命名空间中。
【解决方案2】:

我找到了答案。可以在自定义模型绑定器中调用默认验证过程,如下所示,

public abstract class PaginationModelBinder : IModelBinder
{
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
              var model = (PagingParamsVM)bindingContext.Model ?? new PagingParamsVM();
              //model population logic
              .....

              bindingContext.Model = model;

              //following lines invoke default validation on model
              bindingContext.ValidationNode.ValidateAllProperties = true;
              bindingContext.ValidationNode.Validate(actionContext);

              return true;
        }
}

感谢大家的支持。

【讨论】:

  • @Sajith 我也试过了,但是模型中使用的子实体没有触发验证。为主模型中使用的实体调用验证的任何选项
  • @MagendranV 我很好奇你是否已经设法找到解决方案,因为我遇到了同样的死胡同。 DataAnnotations 验证对于 1 级属性非常有效,但是任何更深的东西都不起作用
猜你喜欢
  • 2012-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多