How to POST XML into MVC Controller? (instead of k

2020-04-02 03:09发布

Using Fiddler I can pass in the body

someXml=ThisShouldBeXml

and then in the controller

    [HttpPost]
    public ActionResult Test(object someXml)
    {
        return Json(someXml);
    }

gets this data as a string

How do I get fiddler to pass XML to the MVC ActionController ? If I try setting the value in the body as raw xml it does not work..

And for bonus points how do I do this from VBscript/Classic ASP?

I currently have

DataToSend = "name=JohnSmith"

          Dim xml
         Set xml = server.Createobject("MSXML2.ServerXMLHTTP")
   xml.Open "POST", _
             "http://localhost:1303/Home/Test", _
             False
 xml.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
 xml.send DataToSend

5条回答
兄弟一词,经得起流年.
2楼-- · 2020-04-02 03:15

In order to pass the data as a sting in MVC you have to create your own media type formatter to handle plain text. Then add the formatter to the config section.

To use the new formatter specify the Content-Type for that formatter, like text/plain.

Sample formatter for text

using System;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.IO;
using System.Text;

namespace SampleMVC.MediaTypeFormatters
{
    public class TextMediaTypeFormmatter : XmlMediaTypeFormatter
    {
        private const int ByteChunk = 1024;
        private UTF8Encoding StringEncoder = new UTF8Encoding();

        public TextMediaTypeFormmatter()
        {
            base.UseXmlSerializer = true;
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
        }

        public override bool CanReadType(Type type)
        {
            if (type == typeof(string))
            {
                return true;
            }
            return false;
        }

        public override bool CanWriteType(Type type)
        {
            if (type == typeof(string))
            {
                return true;
            }
            return false;
        }

        public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger)
        {
            StringBuilder StringData = new StringBuilder();
            byte[] StringBuffer = new byte[ByteChunk];
            int BytesRead = 0;

            Task<int> BytesReadTask = readStream.ReadAsync(StringBuffer, 0, ByteChunk);
            BytesReadTask.Wait();

            BytesRead = BytesReadTask.Result;
            while (BytesRead != 0)
            {
                StringData.Append(StringEncoder.GetString(StringBuffer, 0, BytesRead));
                BytesReadTask = readStream.ReadAsync(StringBuffer, 0, ByteChunk);
                BytesReadTask.Wait();

                BytesRead = BytesReadTask.Result;
            }

            return Task<object>.Run(() => BuilderToString(StringData));
        }

        private object BuilderToString(StringBuilder StringData)
        {
            return StringData.ToString();
        }

        public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
        {
            byte[] StringBuffer = StringEncoder.GetBytes((string)value);
            return writeStream.WriteAsync(StringBuffer, 0, StringBuffer.Length);
        }
    }
}

Controller method:

[HttpPost]
public async Task<HttpResponseMessage> UsingString([FromBody]string XmlAsString)
{
    if (XmlAsString == null)
    {
        return this.Request.CreateResponse(HttpStatusCode.BadRequest);
    }

    return this.Request.CreateResponse(HttpStatusCode.OK, new { });
}

Setup in the WebApiConfig.cs Register method:

config.Formatters.Add(new TextMediaTypeFormmatter());

Fiddler headers:

User-Agent: Fiddler
Content-Type: text/plain
查看更多
Rolldiameter
3楼-- · 2020-04-02 03:19

MVC Controller is not ideal for such request handling, but that was the task, so let’s get to it. Let's have an XML that I to accept:

<document>
<id>123456</id>
    <content>This is document that I posted...</content>
    <author>Michał Białecki</author>
    <links>
        <link>2345</link>
        <link>5678</link>
    </links>
</document>

I tried a few solutions with built-in parameter deserialization but none seem to work and finally, I went with deserializing a request in a method body. I created a helper generic class for it:

public static class XmlHelper
{
    public static T XmlDeserializeFromString<T>(string objectData)
    {
        var serializer = new XmlSerializer(typeof(T));

        using (var reader = new StringReader(objectData))
        {
            return (T)serializer.Deserialize(reader);
        }
    }
}

I decorated my DTO with xml attributes:

[XmlRoot(ElementName = "document", Namespace = "")]
public class DocumentDto
{
    [XmlElement(DataType = "string", ElementName = "id")]
    public string Id { get; set; }

    [XmlElement(DataType = "string", ElementName = "content")]
    public string Content { get; set; }

    [XmlElement(DataType = "string", ElementName = "author")]
    public string Author { get; set; }

    [XmlElement(ElementName = "links")]
    public LinkDto Links { get; set; }
}

public class LinkDto
{
    [XmlElement(ElementName = "link")]
    public string[] Link { get; set; }
}

And used all of that in a controller:

public class DocumentsController : Controller
{
    // documents/sendDocument
    [HttpPost]
    public ActionResult SendDocument()
    {
        try
        {
            var requestContent = GetRequestContentAsString();
            var document = XmlHelper.XmlDeserializeFromString<DocumentDto>(requestContent);

            return new HttpStatusCodeResult(HttpStatusCode.OK);
        }
        catch (System.Exception)
        {
            // logging
            return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
        }
    }

    private string GetRequestContentAsString()
    {
        using (var receiveStream = Request.InputStream)
        {
            using (var readStream = new StreamReader(receiveStream, Encoding.UTF8))
            {
                return readStream.ReadToEnd();
            }
        }
    }
}

To use it, just send a request using for example Postman. I’m sending POST request to http://yourdomain.com/documents/sendDocument endpoint with xml body mentioned above. One detail worth mentioning is a header. Add Content-Type: text/xml, or request to work.

And it works: working deserialization

You can see the whole post on my blog: http://www.michalbialecki.com/2018/04/25/accept-xml-request-in-asp-net-mvc-controller/

查看更多
Juvenile、少年°
4楼-- · 2020-04-02 03:24

In order to send the request using VBScript I have used the WinHttp object i.e. "WinHttp.WinHttpRequest.5.1".

Below is a function which I wrote and this sends the XML request that you pass in and returns the response:

' -----------------------------------------
' Method: sendRequest()
' Descrip: send the web service request as SOAP msg
' -----------------------------------------
Public Function sendRequest(p_SOAPRequest)
    Const METHOD_NAME = "sendRequest()"
    Dim objWinHttp
    Dim strResponse
    Dim URL
    URL = "http:someURL.com"
    Const WINHTTP_OPTION_SECURITY_FLAGS = 13056 '13056: Ignores all SSL Related errors 
    Const WinHttpRequestOption_SslErrorIgnoreFlags = 4 'http://msdn.microsoft.com/en-us/library/Aa384108

    Set objWinHttp = CreateObject("WinHttp.WinHttpRequest.5.1")

    'Open HTTP connection
    Call objWinHttp.Open("POST", URL, False)

    'Set request headers
    Call objWinHttp.setRequestHeader("Content-Type", m_CONTENT_TYPE)
    Call objWinHttp.setRequestHeader("SOAPAction", URL)

    'Ignore the requirement for a security certificate:
    'http://msdn.microsoft.com/en-us/library/windows/desktop/aa384086(v=vs.85).aspx
    objWinHttp.Option(WinHttpRequestOption_SslErrorIgnoreFlags) = WINHTTP_OPTION_SECURITY_FLAGS

    'Send SOAP request
    On Error Resume Next
    objWinHttp.Send p_SOAPRequest

    If Err Then
        m_objLogger.error(METHOD_NAME & " error " & Err.Number & ": " & Err.Description)
        Err.Clear
    End If

    'disable error handling
    On Error GoTo 0

    'Get XML Response
    strResponse = objWinHttp.ResponseText

    'cleanup
    Set objWinHttp = Nothing

    sendRequest = strResponse
End Function
查看更多
Evening l夕情丶
5楼-- · 2020-04-02 03:32

You cannot directly pass XML data as file to MVC controller. One of the best method is to pass XML data as Stream with HTTP post.

For Posting XML,

  1. Convert the XML data to a Stream and attached to HTTP Header
  2. Set content type to "text/xml; encoding='utf-8'"

Refer to this stackoverflow post for more details about posting XML to MVC Controller

For retrieving XML in the controller, use the following method

[HttpPost] 
public ActionResult Index()
{
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

    if (response.StatusCode == HttpStatusCode.OK)
    {
        // as XML: deserialize into your own object or parse as you wish
        var responseXml = XDocument.Load(response.GetResponseStream());

        //in responseXml variable you will get the XML data
    }
}
查看更多
叼着烟拽天下
6楼-- · 2020-04-02 03:36

This seems to be the way to pay XML to a MVC Controller

How to pass XML as POST to an ActionResult in ASP MVC .NET

I tried to get this to work with WEB API but could not so I have to used the MVC 'Controller' instead.

查看更多
登录 后发表回答