I am trying to find a solution to validate if XML data sent in a POST request are fulfilling a given custom XML schema.
If I use the XmlMediaTypeFormatter
delivered with ASP.NET Web API I don't have a schema validation available, as far as I can see. For example: If I have a model type...
public class Order
{
public string Code { get; set; }
public int Quantity { get; set; }
}
...and a POST action in an ApiController
...
public HttpResponseMessage Post(Order order)
{
if (ModelState.IsValid)
{
// process order...
// send 200 OK response for example
}
else
// send 400 BadRequest response with ModelState errors in response body
}
...I can post the following "wrong" XML data and will get a 200 OK response nevertheless:
User-Agent: Fiddler
Host: localhost:45678
Content-Type: application/xml; charset=utf-8
<Order> <Code>12345</Nonsense> </Order> // malformed XML
Or:
<Order> <CustomerName>12345</CustomerName> </Order> // invalid property
Or:
<Customer> <Code>12345</Code> </Customer> // invalid root
Or:
"Hello World" // no XML at all
etc., etc.
The only point where I have a validation of the request is model binding: In request example 1, 3 and 4 the order
passed into the Post
method is null
, in example 2 the order.Code
property is null
which I could invalidate by testing for order == null
or by marking the Code
property with a [Required]
attribute. I could send this validation result back in the response with a 400 "BadRequest" Http status code and validation messages in the response body. But I cannot tell exactly what was wrong and can't distinguish between the wrong XML in example 1, 3 and 4 (no order
has been posted, that's the only thing I can see) - for instance.
Requiring that an Order
has to be posted with a specific custom XML schema, for example xmlns="http://test.org/OrderSchema.xsd"
, I would like to validate if the posted XML is valid with respect to this schema and, if not, send schema validation errors back in the response. To achieve this I have started with a custom MediaTypeFormatter
:
public class MyXmlMediaTypeFormatter : MediaTypeFormatter
{
// constructor, CanReadType, CanWriteType, ...
public override Task<object> ReadFromStreamAsync(Type type, Stream stream,
HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
var task = Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream))
{
XDocument document = XDocument.Load(streamReader);
// TODO: exceptions must the catched here,
// for example due to malformed XML
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(null, "OrderSchema.xsd");
var msgs = new List<string>();
document.Validate(schemaSet, (s, e) => msgs.Add(e.Message));
// msgs contains now the list of XML schema validation errors
// I want to send back in the response
if (msgs.Count == 0)
{
var order = ... // deserialize XML to order
return (object)order;
}
else
// WHAT NOW ?
}
});
return task;
}
}
This works so far as long as everything is correct.
But I don't know what to do if msgs.Count > 0
. How can I "transfer" this validation result list to the Post
action or how can I create a Http response that contains those XML schema validation messages?
Also I am unsure if a custom MediaTypeFormatter
is the best extensibility point for such a XML schema validation and if my approach isn't the wrong way. Would possibly a custom HttpMessageHandler
/DelegatingHandler
be a better place for this? Or is there perhaps something much simpler out of the box?
HttpResponseException
(see my own answer here). But I agree a bit that a MediaTypeFormatter doesn't seem to be the right place. BTW: Do you know what happens if you have multiple MessageHandlers? I have another one that does basic authentication and would not want that the validation handler kicks in at all if the authentication fails. Do I just have to ensure that the auth handler is before the validation handler in the MessageHandlers collection? – Laresa