Web API 验证不使用自定义模型绑定器触发
Posted
技术标签:
【中文标题】Web API 验证不使用自定义模型绑定器触发【英文标题】:Web API Validation not trigger with custom model binder 【发布时间】: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 或类似的,它就行不通了。所以这里似乎缺少一些东西。 【参考方案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 级属性非常有效,但是任何更深的东西都不起作用以上是关于Web API 验证不使用自定义模型绑定器触发的主要内容,如果未能解决你的问题,请参考以下文章