2013-10-09 40 views
30

使用ASP.NET Web API。如果參數爲空,有沒有辦法自動返回狀態碼400?我發現這個question,但這是一個適用於所有方法的全局解決方案,我希望按每個參數的每個方法執行此操作。Web Api必需參數

因此,舉例來說,這是我目前在做什麼:

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

    // Otherwise do more stuff. 
} 

我真的只是想這樣做(注意所需的屬性):

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

過濾器可以接受嗎? –

+0

是的,我認爲任何聲明性解決方案都可以。 –

回答

15

最後我用是創造我全局註冊自定義過濾器的方法。過濾器檢查'RequiredAttribute'的所有請求參數。如果找到屬性,那麼它會檢查參數是否與請求一起傳遞(非空),如果爲空,則返回狀態碼400。我還爲過濾器添加了一個緩存,以便爲每個請求存儲所需的參數,以避免將來調用反射命中。我驚喜地發現,這也適用於值類型,因爲動作上下文將參數存儲爲對象。

編輯 - 更新的解決方案基於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

請注意您的緩存。你可能想使用線程安全的'ConcurrentDictionary'而不是普通的'Dictionary',它不是線程安全的! – tecfield

+0

這是否適用於嵌套字段/'POST'模型?即其中參數是某種具有「[必需]」字段的類。 – Zero3

4

集[必需的],然後檢查ModelState以查看它是否爲IsValid。

這將允許所有需要的屬性在同一時間進行測試。

請參閱「在張貼」部分@Model validation in WebAPI

+1

我擔心這種方法,因爲我可能想要處理一個不同於null參數的無效模型。我確實嘗試了一下,看它是否會起作用,但沒有。因爲該對象爲空,所以從未添加到模型中,因此從未發生過驗證。 –

+0

您是否將模型中的可選參數類型聲明爲可空?不可爲空的基元上的[必需]返回默認值。另外,參數的排序很重要。所有必需的參數必須在可選參數之前。只是好奇,因爲這對我有用。當然,如果你想區分無效模型和空參數,這是無關緊要的。無論如何你仍然需要在某個時候檢查null。 –

+0

我確實聲明瞭可選類型爲空。在可選參數之前,我沒有必要的參數,所以一定是這個問題。 –

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

接受的解決方案需要獨自去報到的任何錯誤。對於MVC5更合適的方法是,讓控制器手柄(通過模型驗證)的任何錯誤的報告,又名是這樣的:

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插上普遍:

config.Filters.Add(new ValidateParametersAttribute());