Upload a XML file from vue with axios to asp .net core

Viewed 602

I'm struggling with uploading an xml file with axios to my asp .net server. I'm using the following code from the vue side to get an xml file, which is working and afterwards upload it to my server:

uploadXmlFile(file: any) {
    const rawFile = new XMLHttpRequest();
    rawFile.open('GET', file, false);
    rawFile.onreadystatechange = () => {
        if (rawFile.readyState === 4) {
            if (rawFile.status === 200 || rawFile.status === 0) {
                const allText = rawFile.responseText;

                axios.post(`${url}`, rawFile, {
                headers: {
                    'Content-Type': 'application/xml',
                    'Accept-Language': 'application/xml',
                },
                });
            }
        }
    };
    rawFile.send(null);
}

On the asp .net side I got this function:

[HttpPost]
public IActionResult Post(object xml)
{
    // Do something with the xml
    ...
}

Uploading the file results in a code 415 : Unsupported Media Type.

I found some suggestions to add the xml formatter to my project, which didn't work and furthermore I don't want to parse the xml file, I just want to save the file to my filesystem.

I tried to upload the rawFile and the parsed text in combination with different media types, e.g. text/plain, text/xml and application/xml

I also tried to add the accept-language

I also tried the proposed way from the microsoft documentation with this function header: public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> files)

And I tried to debug this with Postman, uploading a simple image, which gave me the same error.

2 Answers

If you write the backend like this, it will always try to do serialize the content to JSON, because the default serializer. If you send content type XML, it won't know how to deserialize your XML into an object.

This is how we usually process file uploads:

[HttpPost]
public async Task<IActionResult> ReceiveFileUpload(CancellationToken cancellationToken = default(CancellationToken))
{
    var contentType = Request.ContentType; // Or HttpContext.Request
    var stream = Request.Body; // Or HttpContext.Request
    // Process file stream in whatever way, e.g. XmlDocument.Load(stream);
}

This should at least make it work in e.g. Postman.

Your axios client looks almost ok, a couple of things to note:

  • you probably want to post alltext, not rawFile
  • you should remove 'Accept-Language': 'application/xml'
  • we usually use a FileReader to get the file, not quite sure what you are doing and if that is correct.
            axios.post(url, content, {
                headers: {
                    "Content-Type": "application/xml"
                }
            })
<input type='file' accept='text/plain' onchange='openFile(event)'>

var openFile = function(event) {
    var input = event.target;
    var text = "";
    var reader = new FileReader();
    var onload = function(event) {
        text = reader.result;
        console.log(text);

    };

    reader.onload = onload;
    reader.readAsText(input.files[0]);

};

(https://codepen.io/mlasater9/pen/jqQdOM/)

First of all, regarding your Front-end you need to send the allText constant and not the rawFile. So your code will be like this:

axios.post(
    url,
    allText,
    {
        headers: {
            'Content-Type': 'application/xml'
        }
    }
)

Now for your Back-end, ASP.NET core does not recognize xml by default. You need to go to the file Startup.cs which is on the root of your application and on the method ConfigureServices you need to add services.AddXmlSerializerFormatters(). So your code will be like this:

services
    .AddMvc()
    .AddXmlSerializerFormatters()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); // this depends on your .net core version

You must also create a class model for your xml to be parsed. For example, if you have an xml like this

<document>
    <id>12345</id>
    <content>This is a Test</content>
    <author>vchan</author>
</document>

you should have a class model like the one below

[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; }
}

Also, .net can understand by its own the raw xml body and thus, your controller's action should be like below

[HttpPost]
public IActionResult TestMethod([FromBody] DocumentDto xml) {
    // Do something with the xml
}

Be advised though that you should always use the HTTP header Content-Type: application/xml for your request in order for the .net to understand, parse and deserialize it. You do not need to reinvent the wheel for each different action which you are going to implement, .net does that for you!

Related