2013-10-09 34 views
30

ASP.NET Web APIを使用しています。パラメータがnullの場合、ステータスコード400を自動的に返す方法はありますか?私はこれを発見したquestionしかし、それはすべてのメソッドに適用されるグローバルな解決策です、私はこれをパラメータごとのメソッドごとに行いたいです。Web API必須パラメータ

public HttpResponseMessage SomeMethod([Required] SomeNullableParameter parameter) 
{ 
    // Do stuff. 
} 
+0

フィルターは問題ありませんか? –

+0

はい、宣言的な解決策があればいいと思います。 –

答えて

15

@「アンダーポスティング」を参照してください。フィルタは、 'RequiredAttribute'のすべての要求パラメータをチェックします。属性が見つかった場合は、パラメータが要求と一緒に渡されたかどうか(nullではない)をチェックし、nullの場合はステータスコード400を返します。また、将来の呼び出しで反射ヒットを避けるために、各リクエストに必要なパラメータを格納するためにフィルタにキャッシュを追加しました。私は、アクションコンテキストがパラメータをオブジェクトとして格納しているので、値型に対してもこれが機能することにうれしいことに驚いていました。

EDIT - tecfieldさんのコメント

public class RequiredParametersFilter : ActionFilterAttribute 
{ 
    // Cache used to store the required parameters for each request based on the 
    // request's http method and local path. 
    private readonly ConcurrentDictionary<Tuple<HttpMethod, string>, List<string>> _Cache = 
     new ConcurrentDictionary<Tuple<HttpMethod, string>, List<string>>(); 

    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     // Get the request's required parameters. 
     List<string> requiredParameters = this.GetRequiredParameters(actionContext);  

     // If the required parameters are valid then continue with the request. 
     // Otherwise, return status code 400. 
     if(this.ValidateParameters(actionContext, requiredParameters)) 
     { 
      base.OnActionExecuting(actionContext); 
     } 
     else 
     { 
      throw new HttpResponseException(HttpStatusCode.BadRequest); 
     } 
    } 

    private bool ValidateParameters(HttpActionContext actionContext, List<string> requiredParameters) 
    { 
     // If the list of required parameters is null or containst no parameters 
     // then there is nothing to validate. 
     // Return true. 
     if (requiredParameters == null || requiredParameters.Count == 0) 
     { 
      return true; 
     } 

     // Attempt to find at least one required parameter that is null. 
     bool hasNullParameter = 
      actionContext 
      .ActionArguments 
      .Any(a => requiredParameters.Contains(a.Key) && a.Value == null); 

     // If a null required paramter was found then return false. 
     // Otherwise, return true. 
     return !hasNullParameter; 
    } 

    private List<string> GetRequiredParameters(HttpActionContext actionContext) 
    { 
     // Instantiate a list of strings to store the required parameters. 
     List<string> result = null; 

     // Instantiate a tuple using the request's http method and the local path. 
     // This will be used to add/lookup the required parameters in the cache. 
     Tuple<HttpMethod, string> request = 
      new Tuple<HttpMethod, string>(
       actionContext.Request.Method, 
       actionContext.Request.RequestUri.LocalPath); 

     // Attempt to find the required parameters in the cache. 
     if (!this._Cache.TryGetValue(request, out result)) 
     { 
      // If the required parameters were not found in the cache then get all 
      // parameters decorated with the 'RequiredAttribute' from the action context. 
      result = 
       actionContext 
       .ActionDescriptor 
       .GetParameters() 
       .Where(p => p.GetCustomAttributes<RequiredAttribute>().Any()) 
       .Select(p => p.ParameterName) 
       .ToList(); 

      // Add the required parameters to the cache. 
      this._Cache.TryAdd(request, result); 
     } 

     // Return the required parameters. 
     return result; 
    } 

} 
+6

キャッシュには注意してください。スレッドセーフではない通常の 'Dictionary'の代わりにスレッドセーフ' ConcurrentDictionary'を使用したいかもしれません! – tecfield

+0

これはネストされたフィールド/ 'POST'モデルで機能しますか?私。ここで、パラメータは '[Required]'のフィールドを持つ何らかの種類のクラスです。 – Zero3

4

セット[:

public HttpResponseMessage SomeMethod(SomeNullableParameter parameter) 
{ 
    if (parameter == null) 
     throw new HttpResponseException(HttpStatusCode.BadRequest); 

    // Otherwise do more stuff. 
} 

私は本当にちょうどこのような何か(必要な属性に注目してください)を行いたいと思います:

ので、例えば、これは私が現在やっているものですModelStateをチェックしてIsValidかどうかを確認します。

これにより、必要なすべてのプロパティを同時にテストできます。

は、私が使用して終了したアプローチは、私はグローバルに登録カスタムフィルタを作成することでしたModel validation in WebAPI

+1

ヌルパラメータとは異なる無効なモデルを処理したいので、私はこのアプローチに懸念がありました。私はそれが動作するかどうかを確認しようとしましたが、それはしませんでした。オブジェクトはnullだったので、モデルには一度も追加されませんでしたので、検証は行われませんでした。 –

+0

モデルでオプションのパラメータ型をnullableとして宣言しましたか? null不可能なプリミティブの[必須]はデフォルト値を返します。また、パラメータの順序付けも重要です。必要なすべてのパラメータは、オプションのパラメータに先行する必要があります。ちょうど興味がある、これは私のために働いているので。もちろん、無効なモデルとnullパラメータを区別したい場合は、これはすべて無関係です。とにかく、ある時点でヌルをチェックする必要があります。 –

+0

オプションの型をnullableとして宣言しました。オプションのパラメータの前に必要なパラメータがないため、問題になっていたはずです。 –

0

asp.netコアのソリューションに基づいて更新ソリューション...

[AttributeUsage(AttributeTargets.Method)] 
public sealed class CheckRequiredModelAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext context) 
    { 
     var requiredParameters = context.ActionDescriptor.Parameters.Where(
      p => ((ControllerParameterDescriptor)p).ParameterInfo.GetCustomAttribute<RequiredModelAttribute>() != null).Select(p => p.Name); 

     foreach (var argument in context.ActionArguments.Where(a => requiredParameters.Contains(a.Key, StringComparer.Ordinal))) 
     { 
      if (argument.Value == null) 
      { 
       context.ModelState.AddModelError(argument.Key, $"The argument '{argument.Key}' cannot be null."); 
      } 
     } 

     if (!context.ModelState.IsValid) 
     { 
      var errors = context.ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage); 
      context.Result = new BadRequestObjectResult(errors); 
      return; 
     } 

     base.OnActionExecuting(context); 
    } 
} 

[AttributeUsage(AttributeTargets.Parameter)] 
public sealed class RequiredModelAttribute : Attribute 
{ 
} 

services.AddMvc(options => 
{ 
    options.Filters.Add(typeof(CheckRequiredModelAttribute)); 
}); 

public async Task<IActionResult> CreateAsync([FromBody][RequiredModel]RequestModel request, CancellationToken cancellationToken) 
{ 
    //... 
} 
0

受け入れソリューションは、すべてのエラーを折り返し報告するそれ自体の上にそれを取ります。

using System.ComponentModel.DataAnnotations; 
using System.Linq; 
using System.Web.Http.Controllers; 
using System.Web.Http.Filters; 
using System.Web.Http.ModelBinding; 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 
public sealed class ValidateParametersAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext context) 
    { 
     var descriptor = context.ActionDescriptor; 
     if (descriptor != null) 
     { 
      var modelState = context.ModelState; 
      foreach (var parameterDescriptor in descriptor.GetParameters()) 
      { 
       EvaluateValidationAttributes(
        suppliedValue: context.ActionArguments[parameterDescriptor.ParameterName], 
        modelState: modelState, 
        parameterDescriptor: parameterDescriptor 
       ); 
      } 
     } 

     base.OnActionExecuting(context); 
    } 

    static private void EvaluateValidationAttributes(HttpParameterDescriptor parameterDescriptor, object suppliedValue, ModelStateDictionary modelState) 
    { 
     var parameterName = parameterDescriptor.ParameterName; 

     parameterDescriptor 
      .GetCustomAttributes<object>() 
      .OfType<ValidationAttribute>() 
      .Where(x => !x.IsValid(suppliedValue)) 
      .ForEach(x => modelState.AddModelError(parameterName, x.FormatErrorMessage(parameterName))); 
    } 
} 

あなたはその後、WebApiConfig.cs経由で普遍的にそれをプラグイン可能性があります:

MVC5のためのより適切なアプローチは、このような何か別名、(モデルの検証を経て)コントローラハンドル何らかのエラーの報告をさせることです
config.Filters.Add(new ValidateParametersAttribute()); 
関連する問題