30

我正在尝试将 Fluent Validation 连接到我的 MVC WEB Api 项目,但它不想工作。

当我使用MyController : Controller-> 工作正常(ModelState.IsValid返回False

但是当我使用MyController :ApiController......什么都没有。

有没有人有如何连接这些的经验?

4

6 回答 6

19

最新版 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();
    }
}
于 2014-01-25T12:36:13.153 回答
14

答案就在这个pull request中。

基本上您需要实现自定义ModelValidation提供程序。

还有几件事需要注意:

  1. Web API 不能与 System.Web.Mvc 命名空间中的 modelValidator 一起使用,只能与 System.Web.Http 中的那些一起使用,如下所述:

    使用自定义 DataAnnotationsModelValidatorProvider 进行服务器端验证

  2. 你不要像这样添加它:

    ModelValidatorProviders.Providers.Add(new WebApiFluentValidationModelValidatorProvider());`
    

    但是像这样:

    GlobalConfiguration.Configuration.Services.Add(typeof(System.Web.Http.Validation.ModelValidatorProvider), new WebApiFluentValidationModelValidatorProvider());`
    
于 2012-10-24T08:04:13.720 回答
4

我找到了另一个在 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);
        }
    }
}

该属性可以作为全局过滤器添加到单个控制器/操作或基类中。

这段代码当然可以改进,但到目前为止它对我很有帮助,所以我想让其他人也可以使用它。以下是它的一些缺点:

  1. 未验证空输入。我认为这将是一个更大的问题,但实际上它在我们的应用程序中根本不会发生太多(如果有的话)。我的控制器为空输入抛出 ArgumentNullExceptions,这将向客户端返回 500,通知客户端输入不能为空。
  2. 我不能在我的控制器中使用 ModelState。但是,在验证所需的输入是非空的之后,我已经知道 ModelState 是有效的,所以这实际上可以用来简化代码。但对于开发人员来说,知道不要使用它很重要。
  3. 现在这个实现是为 AttributedValidatorFactory 硬编码的。这应该是抽象的,但到目前为止,它在我的优先级列表中非常低。
于 2013-02-28T15:53:12.070 回答
3

当我想解决这个问题时,我想这样做,以便可以将相同的验证器实例用于 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;

抱歉,这有点长,但希望它可以帮助某人。

于 2013-02-22T15:02:27.670 回答
2

在 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/

于 2016-12-06T21:36:26.757 回答
-4

最新版本的 Fluent Validation 不支持 Mvc 4 或 Web Api。读这个。

于 2012-10-20T22:00:18.133 回答