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());
 

 

亲测可用,如有问题,请留言。

posted @ 2016-01-29 17:15  Kuningasic  阅读(398)  评论(0编辑  收藏  举报