30

Fluent Validation を MVC WEB Api プロジェクトにフックしようとしていますが、うまくいきません。

私が使用するとMyController : Controller->正常に動作します(ModelState.IsValid戻りますFalse

しかし、私が使用するとMyController :ApiController...何もありません。

誰もそれらを接続する方法について経験がありますか?

4

6 に答える 6

14

答えはこのプルリクエストにあります。

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およびMetadataとの統合が欠けています。ただし、(ページを再構築するためにMVCで必要とされるように)ModelState全体をクライアントに返す必要のないAPIを構築する場合、単純さのトレードオフに価値があることがわかりました。API入力が無効な場合は常に、プロパティIDとエラーメッセージのリストを含む400BadRequestステータスコードを返します。これを行うには、単純な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. ヌル入力は検証されません。これはもっと問題になると思いましたが、実際には、私たちのアプリでは(たとえあったとしても)それほど多くは発生しません。私のコントローラーは、null入力に対してArgumentNullExceptionsをスローします。これにより、クライアントに500が返され、入力をnullにすることはできないことをクライアントに通知します。
  2. コントローラでModelStateを使用できません。しかし、必要な入力がnullでないことを検証した後、ModelStateが有効であることをすでに知っているので、これは実際にコードを単純化するのに役立つ可能性があります。ただし、開発者はそれを使用しないことを知っておくことが重要です。
  3. 現在、この実装はAttributedValidatorFactory用にハードコーディングされています。これは抽象化する必要がありますが、これまでのところ、優先順位リストではかなり低くなっています。
于 2013-02-28T15:53:12.070 に答える
3

これを解決しようとしていたので、MVCとWebAPIに同じバリデーターインスタンスを使用できるようにしたいと思いました。これは、2つの工場を作って一緒に使うことで実現できました。

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とWebAPIの両方の検証を実行する方法を理解することでした。最終的に、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 に答える
-4

Fluent Validation の最新バージョンは、Mvc 4 または Web Api をサポートしていません。これを読んでください

于 2012-10-20T22:00:18.133 に答える