我正在尝试将 Fluent Validation 连接到我的 MVC WEB Api 项目,但它不想工作。
当我使用MyController : Controller
-> 工作正常(ModelState.IsValid
返回False
)
但是当我使用MyController :ApiController
......什么都没有。
有没有人有如何连接这些的经验?
我正在尝试将 Fluent Validation 连接到我的 MVC WEB Api 项目,但它不想工作。
当我使用MyController : Controller
-> 工作正常(ModelState.IsValid
返回False
)
但是当我使用MyController :ApiController
......什么都没有。
有没有人有如何连接这些的经验?
最新版 Fluent Validation (5.0.0.1) 支持 web api
只需从 Nuget 安装它并在 Global.asax 中注册它,如下所示:
using FluentValidation.Mvc.WebApi;
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
FluentValidationModelValidatorProvider.Configure();
}
}
答案就在这个pull request中。
基本上您需要实现自定义ModelValidation
提供程序。
还有几件事需要注意:
Web API 不能与 System.Web.Mvc 命名空间中的 modelValidator 一起使用,只能与 System.Web.Http 中的那些一起使用,如下所述:
你不要像这样添加它:
ModelValidatorProviders.Providers.Add(new WebApiFluentValidationModelValidatorProvider());`
但是像这样:
GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Validation.ModelValidatorProvider), new WebApiFluentValidationModelValidatorProvider());`
我找到了另一个在 Web API 中使用 FluentValidation 的简单解决方案,但它缺乏与 ModelState 和元数据的集成。然而,当构建一个不需要将整个 ModelState 返回给客户端的 API 时(正如 MVC 中重建页面所需要的那样),我发现为了简单而进行权衡是值得的。每当 API 输入无效时,我都会返回 400 Bad Request 状态代码以及属性 ID 和错误消息列表。为此,我使用了一个简单的 ActionFilterAttribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateInputsAttribute : ActionFilterAttribute
{
private static readonly IValidatorFactory ValidatorFactory = new AttributedValidatorFactory();
public override void OnActionExecuting(HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
var errors = new Dictionary<string, string>();
foreach (KeyValuePair<string, object> arg in actionContext.ActionArguments.Where(a => a.Value != null))
{
var argType = arg.Value.GetType();
IValidator validator = ValidatorFactory.GetValidator(argType);
if (validator != null)
{
var validationResult = validator.Validate(arg.Value);
foreach (ValidationFailure error in validationResult.Errors)
{
errors[error.PropertyName] = error.ErrorMessage;
}
}
}
if (errors.Any())
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, errors);
}
}
}
该属性可以作为全局过滤器添加到单个控制器/操作或基类中。
这段代码当然可以改进,但到目前为止它对我很有帮助,所以我想让其他人也可以使用它。以下是它的一些缺点:
当我想解决这个问题时,我想这样做,以便可以将相同的验证器实例用于 MVC 和 Web API。我能够通过制造两个工厂并将它们一起使用来实现这一点。
MVC 工厂:
public class MVCValidationFactory : ValidatorFactoryBase
{
private readonly IKernel _kernel;
public MVCValidationFactory(IKernel kernel)
{
_kernel = kernel;
}
public override IValidator CreateInstance(Type validatorType)
{
var returnType = _kernel.TryGet(validatorType);
return returnType as IValidator;
}
}
API工厂:
public class WebAPIValidationFactory : ModelValidatorProvider
{
private readonly MVCValidationFactory _mvcValidationFactory;
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public WebAPIValidationFactory(MVCValidationFactory mvcValidationFactory)
{
_mvcValidationFactory = mvcValidationFactory;
}
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders)
{
try
{
var type = GetType(metadata);
if (type != null)
{
var fluentValidator =
_mvcValidationFactory.CreateInstance(typeof(FluentValidation.IValidator<>).MakeGenericType(type));
if (fluentValidator != null)
{
yield return new FluentValidationModelValidator(validatorProviders, fluentValidator);
}
}
}
catch (Exception ex)
{
Log.Error(ex);
}
return new List<ModelValidator>();
}
private static Type GetType(ModelMetadata metadata)
{
return metadata.ContainerType != null ? metadata.ContainerType.UnderlyingSystemType : null;
}
然后,诀窍是弄清楚如何为 MVC 和 Web API 运行验证。我最终为使用 ModelValidator 签名的 IValidator<> 创建了一个包装器。
public class FluentValidationModelValidator : ModelValidator
{
public IValidator innerValidator { get; private set; }
public FluentValidationModelValidator(
IEnumerable<ModelValidatorProvider> validatorProviders, IValidator validator)
: base(validatorProviders)
{
innerValidator = validator;
}
public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container)
{
if (InnerValidator != null && container != null)
{
var result = innerValidator.Validate(container);
return GetResults(result);
}
return new List<ModelValidationResult>();
}
private static IEnumerable<ModelValidationResult> GetResults(FluentValidation.Results.ValidationResult result)
{
return result.Errors.Select(error =>
new ModelValidationResult
{
MemberName = error.PropertyName,
Message = error.ErrorMessage
}));
}
}
最后一部分是连接 Global.asax 中的验证器:
MVCValidationFactory mvcValidationFactory = new MVCValidationFactory(KernelProvider.Instance.GetKernel());
GlobalConfiguration.Configuration.Services.Add(
typeof(ModelValidatorProvider),
new WebAPIValidationFactory(mvcValidationFactory));
ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(mvcValidationFactory));
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
抱歉,这有点长,但希望它可以帮助某人。
在 WebApiConfig 添加两行
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// snip...
//Fluent Validation
config.Filters.Add(new ValidateModelStateFilter());
FluentValidationModelValidatorProvider.Configure(config);
}
}
创建一个模型和一个验证器,如下所示 -
[Validator(typeof(PersonCreateRequestModelValidator))]
public class PersonCreateRequestModel
{
public Guid PersonId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
public class PersonCreateRequestModelValidator : AbstractValidator
{
//Simple validator that checks for values in Firstname and Lastname
public PersonCreateRequestModelValidator()
{
RuleFor(r => r.Firstname).NotEmpty();
RuleFor(r => r.Lastname).NotEmpty();
}
}
这就是你所需要的。只需像往常一样编写控制器即可。
public IHttpActionResult Post([FromBody]PersonCreateRequestModel requestModel)
{
//snip..
//return Ok(some new id);
}
如果你想要一个完整的源代码示例,你可以在这里获取 - http://NoDogmaBlog.bryanhogan.net/2016/12/fluent-validation-with-web-api-2/
最新版本的 Fluent Validation 不支持 Mvc 4 或 Web Api。读这个。