2013-06-14 27 views
6

Tôi có một lớp DTO trông, ví dụ, như thế này:NET Web API: attribute xác nhận mức Lớp gây Web API để ném ArgumentNullException nếu dụ là null

public class ExampleDto 
{ 
    [DataMember(Name = "Date", IsRequired = true, Order = 1), Required] 
    public DateTime Date { get; set; } 

    [DataMember(Name = "ParentExample", IsRequired = false, Order = 2, EmitDefaultValue = false)] 
    public Guid? ParentExampleId { get; set; } 
} 

Nếu, như một ví dụ, một dùng cung cấp một ngày không chính xác, như thế này:

<?xml version="1.0" encoding="UTF-8" ?> 
<ExampleDto xmlns="http://customurl/"> 
     <Date>2012-05-25T18:23:INCORRECTDATE</Date> 
     <ParentExample>B62F10A8-4998-4626-B5B0-4B9118E11BEC</ParentExample> 
</ExampleDto> 

hoặc đơn giản chỉ là một cơ thể trống rỗng, sau đó lập luận ExampleDto chuyển vào hành động sẽ được null (và trong trường hợp thứ nhất, các ModelState sẽ có lỗi).

tôi áp dụng một CustomValidationAttribute đến lớp, vì vậy việc khai báo lớp trông như thế này:

[CustomValidation(typeof(CustomExampleValidator), "Validate")] 
public class ExampleDto 

Bây giờ tôi đã thêm này, nếu đối số ExampleDto là null (vì một cơ thể trống rỗng, hoặc một vấn đề serialization), một ArgumentNullException được ném:

<?xml version="1.0" encoding="UTF-8"?> 
<Response xmlns="http://customurl" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
    <Type>Failure</Type> 
    <Message>An unknown error has occurred</Message> 
    <Errors> 
     <Error> 
      <Message>System.ArgumentNullException</Message> 
      <MessageDetail>Value cannot be null. Parameter name: 
       instance</MessageDetail> 
      <StackTrace> at System.ComponentModel.DataAnnotations.ValidationContext..ctor(Object 
       instance, IServiceProvider serviceProvider, 
       IDictionary`2 items) at System.Web.Http.Validation.Validators.DataAnnotationsModelValidator.Validate(ModelMetadata 
       metadata, Object container) at System.Web.Http.Validation.DefaultBodyModelValidator.ShallowValidate(ModelMetadata 
       metadata, ValidationContext validationContext, 
       Object container) at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata 
       metadata, ValidationContext validationContext, 
       Object container) at System.Web.Http.Validation.DefaultBodyModelValidator.Validate(Object 
       model, Type type, ModelMetadataProvider metadataProvider, 
       HttpActionContext actionContext, String keyPrefix) 
       at System.Web.Http.ModelBinding.FormatterParameterBinding.&lt;&gt;c__DisplayClass1.&lt;ExecuteBindingAsync&gt;b__0(Object 
       model) at System.Threading.Tasks.TaskHelpersExtensions.&lt;&gt;c__DisplayClass36`1.&lt;&gt;c__DisplayClass38.&lt;Then&gt;b__35() 
       at System.Threading.Tasks.TaskHelpersExtensions.&lt;&gt;c__DisplayClass49.&lt;ToAsyncVoidTask&gt;b__48() 
       at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 
       func, CancellationToken cancellationToken)</StackTrace> 
     </Error> 
    </Errors> 
</Response> 

Reflector cho thấy rằng một tấm séc đối số null được thực hiện đối với các đối tượng trong các nhà xây dựng của ValidationContext, ngay trước khi CustomValidationAttribute được thực thi. Điều này có vẻ hơi kỳ lạ, vì các đối số null được chấp nhận như là các đối số cho các hành động của bộ điều khiển, phải không? Tôi nghĩ rằng bất kỳ đối số null kiểm tra ở đây NÊN được thực hiện trong mã người dùng hoặc rõ ràng bởi các thuộc tính xác nhận, thay vì bởi khuôn khổ.

Nếu người dùng gửi đúng XML/JSON, thì ngoại lệ này không được ném và CustomValidationAttribute thực thi như mong đợi, nhưng người dùng không phải lúc nào cũng được tin cậy để gửi đúng XML/JSON và sẽ nhận được một tìm kiếm đáng ngờ ArgumentNullException vì những nỗ lực của họ, thay vì tôi có thể tự mình trả lại.

Tôi đang cố gắng tìm bất kỳ ai khác đã trải nghiệm điều này. Có rất nhiều ví dụ về việc áp dụng các trình xác nhận hợp lệ ở cấp thuộc tính, nhưng nó có ý nghĩa hơn đối với tôi để áp dụng xác nhận ở cấp lớp ở đây (vì nhiều thuộc tính được yêu cầu NẾU một thuộc tính cụ thể không phải là rỗng, và một số khác được yêu cầu NẾU tài sản khác nhau không phải là null), và tôi không thể tìm thấy bất cứ điều gì để nói rằng các thuộc tính xác nhận được áp dụng ở cấp lớp không được hỗ trợ.

+1

Là một workaround, Tôi đã thực hiện một cách rõ ràng IValidatableObject để thực hiện xác nhận. Mặc dù vậy, tôi thích sử dụng các thuộc tính xác nhận, vì vậy tôi vẫn đang tìm kiếm giải thích cho điều này. –

+0

Tôi đã cố gắng sao chép vấn đề của bạn nhưng không may mắn ... Hãy xem hình ảnh này: http://tallmaris.com/blog/wp-content/uploads/2013/06/Capture.png. Như bạn có thể thấy giá trị đã đến trình xác thực và nó không phải là rỗng. Khi nó đạt đến bộ điều khiển, 'IsValid' sẽ là false vì ngày của khóa học. Thông tin được đăng cho kết quả đó là: 'var example = new {Chi =" YYY ", PatientStatus = 1, Ngày =" 2012-05-25T18: 23: XXXX ", ParentExample =" B62F10A8-4998-4626-B5B0-4B9118E11BEC "};' (sau đó Json đăng và tải lên). – Tallmaris

+0

Xin chào Tallmaris, cảm ơn vì đã cho nó đi. Ngoại lệ không được ném nếu giá trị không phải là giá trị rỗng, chỉ khi giá trị đó là rỗng.Hãy thử đăng bài với var example = null ;, hoặc gửi json theo cách thủ công và có lỗi cú pháp trong đó. –

Trả lời

1

Tôi gặp vấn đề tương tự. Thật không may với một vài ValidationAttributes. Vì vậy, viết lại tất cả chúng để IValidatableObject ngay trước khi phát hành là không thực sự khả thi. Vì vậy, giải pháp nhanh chóng và dơ bẩn của tôi là để nắm bắt những trường hợp ngoại lệ trong một bộ lọc và gửi lại một phản ứng thích hợp:

public class GeneralExceptionFilterAttribute : ExceptionFilterAttribute 
{ 
    public override void OnException(HttpActionExecutedContext context) 
    { 
     var exceptionType = context.Exception.GetType(); 
     HttpResponseMessage response = null; 

     if(exceptionType == typeof(ArgumentNullException) 
      && context.Exception.StackTrace.TrimStart().StartsWith("at System.ComponentModel.DataAnnotations.ValidationContext..ctor")) 
     { 
      response = new HttpResponseMessage(HttpStatusCode.BadRequest) 
      { 
       Content = new StringContent(context.Exception.Message) 
      }; 
     } 
     else 
     { 
      response = new HttpResponseMessage(HttpStatusCode.InternalServerError) 
      { 
       Content = new StringContent(context.Exception.Message), 
       ReasonPhrase = "Unhandled exception" 
      }; 
     } 

     context.Response = response; 
     _errorLogger.LogError(response?.ReasonPhrase, context.Exception); 
    } 
} 

Và đăng ký filer trên toàn cầu trong WebApiConfig.cs:

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