I have a DataContract which my Web API action method accepts as an action parameter.
public HttpResponseMessage PostMyObject(MyObjectRequestDc objRequest){ ... }
[DataContract]
public class MyObjectRequestDc
{
public MyObjectRequestDc()
{
References = new List<Uri>();
}
[DataMember]
public List<Uri> References { get; set; }
}
One of the properties of the contract is a list of URI objects ('References').
If the client ever submits a request which contains a string that does not resolve to a correct URI, an exception is thrown deep within the framework code (because it can't instantiate the URI class from the given string):
Example Json Input:
{ "References": [ "This is not a valid uri." ] }
Exception Details / Stack Trace
Error details: System.InvalidOperationException: This operation is not supported for a relative URI.
at System.Uri.get_AbsolutePath()
at GetAbsolutePathFromUri(Object )
at System.Web.Http.Metadata.Providers.AssociatedMetadataProvider`1.<>c__DisplayClass3.<GetMetadataForPropertiesImpl>b__0()
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateElements(IEnumerable model, ValidationContext validationContext)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateProperties(ModelMetadata metadata, ValidationContext validationContext)
at System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata metadata, ValidationContext validationContext, Object container)
at System.Web.Http.ModelBinding.FormatterParameterBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(Object model)
at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass49.<ToAsyncVoidTask>b__48()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
The requirement is that the user be returned a Response with code 400 (BadRequest) and a message along the lines of "The referenced URI 'xxx' is not valid".
At the moment, the exception is caught by the GlobalExceptionFilter and a unhelpful 500 is returned.
Ideally, I'd like this scenario to be captured as a Model State error or some other mechanism which will allow me to control the response.
Options I've considered: 1. Create a custom HttpParameterBinding class and handle the exception in there? 2. Change the References property to be a list of String objects and then handle the instantiation of the URI class within the action method of the Controller 3. Catch the exception in the GlobalExceptionFilter explictly and tailor the response from here (this has a rank code smell though...)
Suggestions most welcomed!!!
This is a bug in Web API. It currently will always fail when any public property on the type throws.
It's already been fixed in our current bits: https://aspnetwebstack.codeplex.com/workitem/611 and the fix should be available in our next release.
If you disable validation like this as a workaround:
you might get a better behavior with an invalid model state.
Given that there appears to be no answer to the problem, the work around I am going to employ is my second option:
And then handle any exceptions that occur during the instantiation of the URI myself...