2013-10-09 17 views
30

Sử dụng ASP.NET Web API. Có cách nào để tự động trả về mã trạng thái 400 nếu tham số rỗng không? Tôi tìm thấy điều này question nhưng đó là một giải pháp toàn cầu được áp dụng cho tất cả các phương pháp, tôi muốn làm điều này trên một phương pháp cho mỗi cơ sở tham số.Web Yêu cầu Parameter

Vì vậy, ví dụ, đây là những gì tôi hiện đang thực hiện:

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

    // Otherwise do more stuff. 
} 

tôi sẽ thực sự chỉ muốn làm một cái gì đó như thế này (chú ý các thuộc tính bắt buộc):

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

một bộ lọc có thể chấp nhận được? –

+0

Có, tôi nghĩ rằng bất kỳ giải pháp khai báo nào cũng được. –

Trả lời

15

Phương pháp tôi đã kết thúc sử dụng là tạo ra một bộ lọc tùy chỉnh mà tôi đã đăng ký trên toàn cầu. Bộ lọc kiểm tra tất cả các tham số yêu cầu cho 'RequiredAttribute'. Nếu thuộc tính được tìm thấy thì nó sẽ kiểm tra nếu tham số được truyền với request (không null) và trả về mã trạng thái 400 nếu nó là null. Tôi cũng đã thêm bộ nhớ cache vào bộ lọc để lưu trữ các thông số bắt buộc cho từng yêu cầu để tránh ảnh hưởng phản chiếu trong các cuộc gọi trong tương lai. Tôi đã ngạc nhiên khi thấy rằng điều này làm việc cho các loại giá trị cũng như vì bối cảnh hành động lưu trữ các tham số như các đối tượng.

EDIT - Cập nhật giải pháp dựa trên nhận xét tecfield của

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

Hãy cẩn thận về bộ nhớ cache. bạn có thể muốn sử dụng một 'ConcurrentDictionary' an toàn chỉ thay vì một' Dictionary' bình thường mà không phải là thread-safe! – tecfield

+0

Tính năng này có hoạt động với các trường lồng nhau/mô hình 'POST' không? I E.trong đó tham số là một lớp thuộc loại nào đó có các trường là '[Bắt buộc]'. – Zero3

4

Set [ Bắt buộc] trên thuộc tính trong mô hình của bạn và sau đó kiểm tra ModelState để xem IsValid có phải là mô hình không.

Điều này sẽ cho phép tất cả các thuộc tính cần thiết để được kiểm tra cùng một lúc.

Hãy xem phần "Under-viết bài" phần @Model validation in WebAPI

+1

Tôi đã có mối quan tâm với phương pháp này bởi vì tôi có thể muốn xử lý một mô hình không hợp lệ khác với tham số null. Tôi đã thử nó mặc dù để xem nếu nó sẽ làm việc và nó không. Bởi vì đối tượng là null nó chưa bao giờ được thêm vào mô hình nên việc xác thực không bao giờ xảy ra. –

+0

Bạn có khai báo kiểu tham số tùy chọn là nullable trong mô hình của bạn không? [Bắt buộc] trên các nguyên thủy không thể trả về giá trị mặc định. Ngoài ra, thứ tự của các tham số quan trọng. Tất cả tham số bắt buộc phải đặt trước các thông số tùy chọn. Chỉ cần tò mò, vì điều này là làm việc cho tôi. Tất nhiên điều này là không liên quan, nếu bạn muốn phân biệt giữa mô hình không hợp lệ và tham số null. Bạn vẫn phải kiểm tra null tại một số điểm anyway. –

+0

Tôi đã khai báo kiểu tùy chọn là nullable. Tôi không có tham số bắt buộc trước các tham số tùy chọn do đó phải có vấn đề. –

0

Một giải pháp cho lõi 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

Các giải pháp chấp nhận mất nó khi bản thân để báo cáo lại bất kỳ lỗi . Một cách tiếp cận phù hợp hơn cho MVC5 là để cho tay cầm điều khiển (thông qua xác nhận mô hình) các báo cáo bất kỳ lỗi nào, hay còn gọi là một cái gì đó như thế này:

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))); 
    } 
} 

Sau đó, bạn có thể cắm nó trong phổ biến qua WebApiConfig.cs:

config.Filters.Add(new ValidateParametersAttribute()); 
Các vấn đề liên quan