I have a DTO class that looks, for example, like this:
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; }
}
If, as an example, a user provides an incorrect date, such as this:
<?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>
or simply just an empty body, then the ExampleDto argument passed into the action will be null (and in the former case, the ModelState will have errors).
I applied a CustomValidationAttribute to the class, so the class declaration looks like this:
[CustomValidation(typeof(CustomExampleValidator), "Validate")]
public class ExampleDto
Now that I've added this, if the ExampleDto argument is null (because of an empty body, or a serialization problem), an ArgumentNullException is thrown:
<?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.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(Object
model) at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass36`1.<>c__DisplayClass38.<Then>b__35()
at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass49.<ToAsyncVoidTask>b__48()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1
func, CancellationToken cancellationToken)</StackTrace>
</Error>
</Errors>
</Response>
Reflector shows that a null argument check is performed against the object in the constructor of the ValidationContext, just before the CustomValidationAttribute is executed. This seems a bit bizarre, because null arguments are acceptable as arguments to controller actions, no? I think any null argument checks here SHOULD be performed in user code or explicitly by validation attributes, instead of by the framework.
If the user submits correct XML/JSON, then this exception isn't thrown and the CustomValidationAttribute executes as expected, but the users can't always be trusted to submit correct XML/JSON, and will get an obtuse looking ArgumentNullException for their efforts, instead of one that I'm able to return myself.
I'm struggling to find anyone else who has experienced this. There are plenty of examples of applying "compound" validators at property level, but it makes more sense for me to apply the validation at class level here (because multiple properties are required IF a specific property is not null, and others are required IF a different property is not null), and I can't find anything to say that validation attributes applied at class level are unsupported.
IsValid
will be false because of the date of course. The posted info for that result were:var example = new { Chi = "YYY", PatientStatus = 1, Date = "2012-05-25T18:23:XXXX", ParentExample = "B62F10A8-4998-4626-B5B0-4B9118E11BEC" };
(then Json serialized and uploaded). – Fosdick