我正在尝试为我的 ASP MVC 项目提供一个简单的 RESTful API。我无法控制此 API 的客户端,它们将通过 POST 方法传递 XML,该方法将包含在服务器端执行某些操作所需的信息,并返回带有操作结果的 XML。我在发回 XML 时没有遇到问题,问题是通过 POST 接收 XML。我看过一些 JSON 示例,但由于我不会控制我的客户端(从我的角度来看,它甚至可能是 telnet),我认为 JSON 不起作用。我对么?

我见过这样的例子:客户端简单地构建正确的表单格式作为请求正文的一部分,然后 ASP 解析消息,数据以 FormCollection 形式提供(?param1=value1&param2=value2&等)。但是,我想将纯 XML 作为消息正文的一部分传递。

感谢您的帮助,

有帮助吗?

解决方案 2

这可以通过使用ActionFilterAttribute来完成。操作过滤器基本上前或操作结果后相交的请求。所以,我只是专为POST行为所产生的自定义操作过滤属性。这里是我做过什么:

public class RestAPIAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContextBase httpContext = filterContext.HttpContext;
        if (!httpContext.IsPostNotification)
        {
            throw new InvalidOperationException("Only POST messages allowed on this resource");
        }
        Stream httpBodyStream = httpContext.Request.InputStream;

        if (httpBodyStream.Length > int.MaxValue)
        {
            throw new ArgumentException("HTTP InputStream too large.");
        }

        int streamLength = Convert.ToInt32(httpBodyStream.Length);
        byte[] byteArray = new byte[streamLength];
        const int startAt = 0;

        /*
         * Copies the stream into a byte array
         */
        httpBodyStream.Read(byteArray, startAt, streamLength);

        /*
         * Convert the byte array into a string
         */
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < streamLength; i++)
        {
            sb.Append(Convert.ToChar(byteArray[i]));
        }

        string xmlBody = sb.ToString();

        //Sends XML Data To Model so it could be available on the ActionResult

        base.OnActionExecuting(filterContext);
    }
}

然后在您的控制器上的操作结果的方法,你应该做的是这样的:

    [RestAPIAttribute]
    public ActionResult MyActionResult()
    {
        //Gets XML Data From Model and do whatever you want to do with it
    }

希望这有助于别人,如果你觉得有更优雅的方式做到这一点,让我知道。

其他提示

@Freddy - 喜欢你的方法和用下面的代码就可以改进,以简化流读出:

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContextBase httpContext = filterContext.HttpContext;
        if (!httpContext.IsPostNotification)
        {
            throw new InvalidOperationException("Only POST messages allowed on this resource");
        }

        Stream httpBodyStream = httpContext.Request.InputStream;
        if (httpBodyStream.Length > int.MaxValue)
        {
            throw new ArgumentException("HTTP InputStream too large.");
        }

        StreamReader reader = new StreamReader(httpBodyStream, Encoding.UTF8);
        string xmlBody = reader.ReadToEnd();
        reader.Close();

        filterContext.ActionParameters["message"] = xmlBody;

        // Sends XML Data To Model so it could be available on the ActionResult
        base.OnActionExecuting(filterContext);
    }

然后,在控制器可以访问XML作为字符串:

[RestAPIAttribute]    
public ActionResult MyActionResult(string message)    
{         

}

我知道你可以创建一个自定义值提供程序工厂。这将让你也验证模型时,他们正试图拯救他们之前公布。 菲尔哈克有一个博客帖子大约在同一概念的JSON版本。唯一的问题是,我不知道如何实现这一个同样的事情为XML。

IMO 实现此目的的最佳方法是编写一个自定义值提供程序,这是一个处理请求到表单字典的映射的工厂。您只需继承 ValueProviderFactory 并处理类型为“text/xml”或“application/xml”的请求。

更多信息:

菲尔·哈克

我的博客

微软软件定义网络

protected override void OnApplicationStarted()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);

    ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
    ValueProviderFactories.Factories.Add(new XmlValueProviderFactory());
}

XmlValueProviderFactory

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web.Mvc;
using System.Xml;
using System.Xml.Linq;

public class XmlValueProviderFactory : ValueProviderFactory
{

    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        var deserializedXml = GetDeserializedXml(controllerContext);

        if (deserializedXml == null) return null;

        var backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

        AddToBackingStore(backingStore, string.Empty, deserializedXml.Root);

        return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);

    }

    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 uniqueElements = new List<String>();
        var totalElments = 0;
        foreach (XElement element in xmlDoc.Elements())
        {
            if (!uniqueElements.Contains(element.Name.LocalName))
                uniqueElements.Add(element.Name.LocalName);
            totalElments++;
        }

        var isArray = (uniqueElements.Count == 1 && totalElments > 1);


        // Add the elements to the backing store
        var elementCount = 0;
        foreach (XElement element in xmlDoc.Elements())
        {
            if (element.HasElements)
            {
                if (isArray)
                    AddToBackingStore(backingStore, MakeArrayKey(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 string MakeArrayKey(string prefix, int index)
    {
        return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
    }

    private static string MakePropertyKey(string prefix, string propertyName)
    {
        if (!string.IsNullOrEmpty(prefix))
            return prefix + "." + propertyName;
        return propertyName;
    }

    private XDocument GetDeserializedXml(ControllerContext controllerContext)
    {
        var contentType = controllerContext.HttpContext.Request.ContentType;
        if (!contentType.StartsWith("text/xml", StringComparison.OrdinalIgnoreCase) &&
            !contentType.StartsWith("application/xml", StringComparison.OrdinalIgnoreCase))
            return null;

        XDocument xml;
        try
        {
            var xmlReader = new XmlTextReader(controllerContext.HttpContext.Request.InputStream);
            xml = XDocument.Load(xmlReader);
        }
        catch (Exception)
        {
            return null;
        }

        if (xml.FirstNode == null)//no xml.
            return null;

        return xml;
    }
}
它们

为什么不能传递XML作为在成形后的字符串?

示例:

public ActionResult SendMeXml(string xml)
{
  //Parse into a XDocument or something else if you want, and return whatever you want.
  XDocument xmlDocument = XDocument.Parse(xml);

  return View();
}

您可以创建一个表单提交,并在单一表单字段发送。

我喜欢从@Freddy从@Bowerm答案和改进。它简洁而保留的形式为基础的行动格式。

但IsPostNotification检查不会在生产代码工作。它不检查HTTP动词作为错误信息似乎暗示,它是剥离出来HTTP上下文当编译调试标志设置为false。这是在这里解释: HttpContext.IsPostNotification是假的时,编译调试是假

我希望这可以节省一个人一天的1/2,由于这个问题的调试路线。这里是没有这种检查溶液:

public class XmlApiAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContextBase httpContext = filterContext.HttpContext;
        // Note: for release code IsPostNotification stripped away, so don't check it!
        // https://stackoverflow.com/questions/28877619/httpcontext-ispostnotification-is-false-when-compilation-debug-is-false            

        Stream httpBodyStream = httpContext.Request.InputStream;
        if (httpBodyStream.Length > int.MaxValue)
        {
            throw new ArgumentException("HTTP InputStream too large.");
        }

        StreamReader reader = new StreamReader(httpBodyStream, Encoding.UTF8);
        string xmlBody = reader.ReadToEnd();
        reader.Close();

        filterContext.ActionParameters["xmlDoc"] = xmlBody;

        // Sends XML Data To Model so it could be available on the ActionResult
        base.OnActionExecuting(filterContext);
    }
}
...
public class MyXmlController 
{ ...
    [XmlApiAttribute]
    public JsonResult PostXml(string xmlDoc)
    {
...

尼斯!,

我在控制器的方法有什么目的来操作XML?

我使用这样:

在actionFilter,我填充与模型:

        .
        .

        string xmlBody = sb.ToString();

        filterContext.Controller.ViewData.Model = xmlBody;

和我的控制器的方法,我得到的型号为:

        string xmlUserResult = ViewData.Model as string;

        XmlSerializer ser = new XmlSerializer(typeof(UserDTO));
        StringReader stringReader = new StringReader(xmlUserResult);
        XmlTextReader xmlReader = new XmlTextReader(stringReader);
        UserDTO userToUpdate = ser.Deserialize(xmlReader) as UserDTO;
        xmlReader.Close();
        stringReader.Close();

这是一个正确实施?

感谢。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top