question

Yasar-8463 avatar image
0 Votes"
Yasar-8463 asked Yasar-8463 commented

Issue receiveing POST request with Content-Type text/xml

I'm kinda new to building web applications. I used the template for a ASP.NET Core Web App (Modell-View-Controller) to create a Web Api for REST POST requests.
It was chosen becuase I want to run it on a respberry pi 4 in the end and was able to select the target system when publishing in the Developer PowerShell in VS19.

I currently have a working POST function for request with the Content-Type application/xml. I have to adjust the server POST function because the request is not changeable.

I used an additional service to be able to work with xmls:

 services.AddMvc(config =>
                 {
                     config.InputFormatters.Insert(0, new XDocumentInputFormatter());
                 }).SetCompatibilityVersion(CompatibilityVersion.Latest).AddXmlSerializerFormatters();

The used POST method:

 [HttpPost]
         [Consumes("application/xml")]
         public void PostApplXML([FromBody] XDocument value)
         {
         }

I did send my request with Fiddler and received all the data I needed. I also checked it in VS and analyzed the received data.

I have an issue regarding a second POST function for Content-Type text/xml;charset=utf-8. I tried various input parameters to be able to receive the data but was not able to see them in my function.

These are the different ways I have already tried with the responses from Fiddler:

 [HttpPost]
        [Consumes("text/xml")]
         public void PostTextXML(HttpRequestMessage request)
         {
           //Fiddler: HTTP/1.1 415 Unsupported Media Type
         }

 [HttpPost]
         [Consumes("text/xml")]
         public void PostTextXML([FromBody] HttpRequestMessage request)
         {
             //Fiddler: HTTP/1.1 415 Unsupported Media Type
         }

 [HttpPost]
         [Consumes("text/xml")]
         public void PostTextXML(string request)
         {
             //Fiddler: Status OK, request = NULL
         }

 [HttpPost]
         [Consumes("text/xml")]
         public void PostTextXML([FromBody] string request)
         {
             //Fiddler: HTTP/1.1 500 Internal Server Error
         }


Edit:
I forgot to mention that I also added an InputFormatter especially for the xml data in the Startup.cs:

 public class XDocumentInputFormatter : InputFormatter, IInputFormatter, IApiRequestFormatMetadataProvider
     {
         public XDocumentInputFormatter()
         {
             SupportedMediaTypes.Add("application/xml");
         }
    
         protected override bool CanReadType(Type type)
         {
             if (type.IsAssignableFrom(typeof(XDocument))) return true;
             return base.CanReadType(type);
         }
    
         public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
         {
             var xmlDoc = await XDocument.LoadAsync(context.HttpContext.Request.Body, LoadOptions.None, CancellationToken.None);
    
             return InputFormatterResult.Success(xmlDoc);
         }
     }


dotnet-csharpdotnet-aspnet-core-mvcdotnet-aspnet-core-webapi
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AgaveJoe avatar image
1 Vote"
AgaveJoe answered Yasar-8463 commented

Does it mean that I have to create a class with the exact XML structure (or only the values I require from that XML?)

Yes. When you add an input parameter in the action method the model binder tries to match the input parameter to the [FromBody] stream. This is a fundamental concept in Web API.

Try removing the input parameter and read the XML body into a string. From there you can do whatever you need with the XML string.


 [HttpPost]
 [Produces("text/xml")]
 [Consumes("text/xml")]
 public async Task<string> PostAsync()
 {
     string xml;
     using (System.IO.StreamReader reader = new System.IO.StreamReader(Request.Body, Encoding.UTF8))
     {
         xml =  await reader.ReadToEndAsync();
     }
    
     return xml;
 }








· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you very much. This is exactly what I needed.
I like the xml string method since I can just filter the string with regex patterns.

I previously tried to access the stream string but without defining the function as async as well as giving the StreamReader the Encoding parameter.
The request.body was just empty for me.

0 Votes 0 ·
AgaveJoe avatar image
1 Vote"
AgaveJoe answered AgaveJoe converted comment to answer

You're struggling with data binding. Web API comes with databinding which coverts the incoming data stream to a string type. This is integrated into the framework.

First, configure the XML formatter in startup.

 services.AddControllers().AddXmlSerializerFormatters();


The data model that will be populated by the XML stream

     public class DataModel
     {
         public int id { get; set; }
         public string value { get; set; }
     }

The action to accept and return XML.

 [HttpPost]
 [Produces("text/xml")]
 public DataModel Post([FromBody] DataModel value)
 {
     value.value = $"From Post {value.value}";
     return value;
 }

The raw xml data sent to the action.

 <DataModel>
     <id>1</id>
     <value>Hello World</value>
 </DataModel>





· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I tried it as you described it but without sending back the DataModel. Even removed the expected content-type.

I'm getting a 400 Bad Request response from Fiddler that way and my breakpoint inside the function does not get triggered.



Maybe there is an issue with my request settings?
I only changed Content-Type from application/xml to text/xml;charset=utf-8 or text/xml since it worked with the first method.

  • POST http://localhost:60000/api/Interface HTTP/1.1

  • User-Agent: Fiddler

  • Content-Type: text/xml;charset=utf-8

  • Host: localhost:60000

  • Content-Length: 341


  • <?xml version="1.0" encoding="utf-8"?><...

0 Votes 0 ·

A 400 error means the client request was not formatted as the server expects. The sample code provided above functions as expected. The HTTP POST uses text/xml.

 POST /api/values HTTP/1.1
 Host: localhost:44352
 Content-Type: text/xml;charset=utf-8
 Content-Length: 113
    
 <?xml version="1.0" encoding="utf-8"?>
 <DataModel>
     <id>1</id>
     <value>Hello World</value>
 </DataModel>
1 Vote 1 ·

Does it mean that I have to create a class with the exact XML structure (or only the values I require from that XML?) that I'm sending to my server and give it to my function as the input value?

My structure looks like this with multiple nodes:

 <1>
   <2 BEGIN="1">
     <3 SEGMENT="1">
            ...
            ...
            Data_I_want_1
            ...
     </3>
     <4 SEGMENT="1">
            ...
            ...
            Data_I_want_2
            ...
     </4>
   </2>
 </1>







0 Votes 0 ·