ASP.NET MVC XML绑定Action参数列表
昨天查看了 ASP.NET MVC 的生命周期,并没有找到类似的解决方案。
不过今天在 stackoverflow上找到了解决方案,没耐心的同学可以直接戳原文拷贝代码,原文地址:How to pass XML as POST to an ActionResult in ASP MVC .NET
看了外国同学的说明,才发现 MVC居然可以根据不同的请求 Content Type来选择 ValueProvider,这样提供了很好的扩展。
MVC本身提供了 JsonValueProviderFactory,顾名思义就是为 Json服务的。那么在这里我们需要提供为 XML服务的ValueProviderFactory。
第一步:创建 XMLValueProviderFactory
public class XmlValueProviderFactory : ValueProviderFactory { private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, XElement xmlDoc) { // Check the keys to see if this is an array or an object var uniqueKeys = new List<string>(); int totalCount = 0; foreach (XElement element in xmlDoc.Elements()) { if (!uniqueKeys.Contains(element.Name.LocalName)) uniqueKeys.Add(element.Name.LocalName); totalCount++; } bool isArray; if (uniqueKeys.Count == 1) { isArray = true; } else if (uniqueKeys.Count == totalCount) { isArray = false; } else { // Not sure how to deal with a XML doc that has some keys the same, but not all // For now don't process this node return; } // Add the elements to the backing store int elementCount = 0; foreach (XElement element in xmlDoc.Elements()) { if (element.HasElements) { if (isArray) { // Omit local name for arrays and add index instead AddToBackingStore(backingStore, $"{prefix}[{elementCount}]", element); } else { AddToBackingStore(backingStore, MakePropertyKey(prefix, element.Name.LocalName), element); } } else { backingStore.Add(MakePropertyKey(prefix, element.Name.LocalName), element.Value); } elementCount++; } } private static XDocument GetDeserializedXml(ControllerContext controllerContext) { var contentType = controllerContext.HttpContext.Request.ContentType; if (!contentType.StartsWith("text/xml", StringComparison.OrdinalIgnoreCase) && !contentType.StartsWith("application/xml", StringComparison.OrdinalIgnoreCase)) { // Are there any other XML mime types that are used? (Add them here) // not XML request return null; } XDocument xml; try { // DTD processing disabled to stop XML bomb attack - if you require DTD processing, read this first: http://msdn.microsoft.com/en-us/magazine/ee335713.aspx var xmlReaderSettings = new XmlReaderSettings {DtdProcessing = DtdProcessing.Prohibit}; var xmlReader = XmlReader.Create(controllerContext.HttpContext.Request.InputStream, xmlReaderSettings); xml = XDocument.Load(xmlReader); } catch (Exception) { return null; } if (xml.FirstNode == null) { // No XML data return null; } return xml; } private static string MakeArrayKey(string prefix, int index) { return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]"; } private static string MakePropertyKey(string prefix, string propertyName) { return (prefix.IsNullOrEmpty()) ? propertyName : prefix + "." + propertyName; } #region ValueProviderFactory Members public override IValueProvider GetValueProvider(ControllerContext controllerContext) { var xmlData = GetDeserializedXml(controllerContext); if (xmlData == null) return null; var backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); AddToBackingStore(backingStore, String.Empty, xmlData.Root); return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture); } #endregion }
该段代码属于网上抄袭,基本没有改,在下觉得这样已经满足需求了,原文地址:Sending XML to an ASP.NET MVC Action Method Argument
PS:如果涉及版权问题,请告知在下。
第二步:在 Global.asmx.cs 的 Application_Start 方法中注册 ValueProviderFactory
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory()); ValueProviderFactories.Factories.Add(new XmlValueProviderFactory());
亲测可用,如有问题,请留言。