Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to POST XML into MVC Controller? (instead of key/value)

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
like image 824
punkouter Avatar asked Jul 30 '13 15:07

punkouter


People also ask

How pass data from model to controller in MVC?

In Solution Explorer, right-click the Controllers folder and then click Add, then Controller. In the Add Scaffold dialog box, click MVC 5 Controller with views, using Entity Framework, and then click Add. Select Movie (MvcMovie. Models) for the Model class.

How do you pass model data to view inside a controller?

The other way of passing the data from Controller to View can be by passing an object of the model class to the View. Erase the code of ViewData and pass the object of model class in return view. Import the binding object of model class at the top of Index View and access the properties by @Model.


4 Answers

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
    }
}
like image 82
Adersh M Avatar answered Nov 16 '22 00:11

Adersh M


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.

like image 40
punkouter Avatar answered Nov 16 '22 02:11

punkouter


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
like image 30
Brian from state farm Avatar answered Nov 16 '22 01:11

Brian from state farm


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/

like image 40
Mik Avatar answered Nov 16 '22 02:11

Mik