我试图找到解决方案来验证在POST请求中发送的XML数据是否满足给定的自定义XML模式。使用ASP.NET WebAPI对POST请求进行XML模式验证
如果我使用随ASP.NET Web API一起提供的XmlMediaTypeFormatter
,我没有可用的模式验证,据我所见。例如:如果我有一个模型类型...
public class Order
{
public string Code { get; set; }
public int Quantity { get; set; }
}
...并在ApiController
一个POST操作...
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
}
...我可以张贴下面的 “错误” XML数据,并仍然得到一个200 OK响应:
User-Agent: Fiddler
Host: localhost:45678
Content-Type: application/xml; charset=utf-8
<Order> <Code>12345</Nonsense> </Order> // malformed XML
或者:
<Order> <CustomerName>12345</CustomerName> </Order> // invalid property
或者:
<Customer> <Code>12345</Code> </Customer> // invalid root
或者:
"Hello World" // no XML at all
等等等等
其中I具有所述请求的确认的唯一点是模型绑定:在请求实施例1,3和4 order
传入Post
方法是null
,在示例2中order.Code
属性是null
我可以通过测试无效或用[Required]
属性标记Code
属性。我可以将此验证结果发送回响应中,并在响应正文中使用400“BadRequest”Http状态代码和验证消息。但是我无法确切地知道出了什么问题,并且无法区分示例1,示例3和示例4中的错误XML(没有发布order
,这是我能看到的唯一一个) - 例如。
要求Order
必须使用特定的自定义XML模式发布,例如xmlns="http://test.org/OrderSchema.xsd"
,我想验证发布的XML对于此模式是否有效,如果不是,则将模式验证错误发回响应。要做到这一点我已经开始使用自定义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;
}
}
到目前为止是这种情况,只要一切是正确的。
但是我不知道该怎么做msgs.Count > 0
。如何将此验证结果列表“转移”到Post
操作或如何创建包含这些XML架构验证消息的Http响应?
此外,我不确定自定义MediaTypeFormatter
是否是此类XML模式验证的最佳可扩展性点,以及如果我的方法不是错误的方法。定制的HttpMessageHandler
/DelegatingHandler
会是更好的选择吗?或者可能有更简单的开箱即用的东西?
感谢您的回复,我会尽力。我昨天通过抛出一个'HttpResponseException'找到了类似的解决方案(请参阅我自己的答案)。但我同意一个MediaTypeFormatter似乎不是正确的地方。顺便说一句:你知道如果你有多个MessageHandlers会发生什么?我有另一个基本身份验证,并不希望如果身份验证失败验证处理程序踢任何人。我是否必须确保验证处理程序在MessageHandlers集合中的验证处理程序之前? – Slauma 2012-08-06 10:49:23
我目前没有得到代码工作。在'msgs.Count> 0'情况下,它以Http 500,stacktrace'EndProcessRequest' - >'OnAsyncHandlerCompletion'结尾,'else'情况起作用。我还不太熟悉Task API和WebAPI以找到修复程序。我尝试了一些随机更改,但没有成功。认证消息处理程序必须是第一个要执行的最后一个。但即使身份验证处理程序中的身份验证失败,我仍会输入验证处理程序。也许我必须检查安全主体,然后跳过处理程序,然后,不知何故...... – Slauma 2012-08-06 14:07:13
@Slauma通过添加多个消息处理程序,您可以获得它们的一个链。只需在验证处理程序之后添加验证处理程序。 – 2012-08-06 14:15:53