5

Trying to get a .net core 2.0 web api HttpPost method to work with xml input.

Expected Result: When the test endpoint is called from Postman, the input parameter (xmlMessage in the below code) should have the value being sent from the Postman HttpPost body.

Actual Result: input parameter is null.

In startup.cs of the web api project, we have the following code:

public class Startup
{
   public Startup(IConfiguration configuration)
   {
      Configuration = configuration;
   }

   public IConfiguration Configuration { get; }

   // This method gets called by the runtime. Use this method to add services to the container.
   public void ConfigureServices(IServiceCollection services)
   {
      services.AddMvc()
      .AddXmlDataContractSerializerFormatters();
   }

   // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
   public void Configure(IApplicationBuilder app, IHostingEnvironment env)
   {
      if (env.IsDevelopment())
      {
         app.UseDeveloperExceptionPage();
      }
      app.UseMvc();
   }
}

In controller:

[HttpPost, Route("test")]
public async Task<IActionResult> Test([FromBody] XMLMessage xmlMessage)
{
    return null; //not interested in the result for now
}

XMLMessage class:

[DataContract]
public class XMLMessage
{
    public XMLMessage()
    {
    }

    [DataMember]
    public string MessageId { get; set; }
}

In Postman Headers:

Content-Type:application/xml

Http Post Body:

<XMLMessage>
  <MessageId>testId</MessageId>
</XMLMessage>

Appreciate any help that could point me in the right direction. Thanks in advance..

6
  • This might help: stackoverflow.com/questions/37424559/… Commented Jul 23, 2018 at 13:52
  • Thanks for the link @RuiJarimba. using XElement as the parameter type gets the xml value into the input parameter xmlMessage. However, I'm interested in getting the xml value directly deserialized to my XMLMessage class object. Commented Jul 23, 2018 at 13:57
  • 2
    Just to help troubleshoot your XMLMessage class, try using the XElement to receive the XML and then write code along the lines of this answer to ensure you've configured the DataContract annotation correctly. It may be that Web API isn't able to match the class type to the incoming XML. Commented Jul 23, 2018 at 14:23
  • @SixtoSaez, that did the trick. I called the Deserialize method and got the error: xmlns="http://schemas.datacontract.org/2004/07/MyWebApi.Controllers" SerializationException: Error in line 1 position 13. Expecting element XMLMessage from namespace http://schemas.datacontract.org/2004/07/MyWebApi.Controllers.. Encountered Element with name XMLMessage, namespace . Once I added the namespace like: <XMLMessage xmlns="http://schemas.datacontract.org/2004/07/MyWebApi.Controllers">, it started working. Thanks a lot. Commented Jul 23, 2018 at 14:40
  • @Matt.G could you please post your full Startup.cs code? Commented Jul 23, 2018 at 16:09

3 Answers 3

4

You should be using XmlRoot/XmlElement instead of the DataContract/DataElement annotations types. Below is what should be changed in order to make it work.

On Startup.cs

public void ConfigureServices(IServiceCollection services){
    services.AddMvc(options =>
    {
        options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
    });
    // Add remaining settings
}

XMLMessage class:

[XmlRoot(ElementName = "XMLMessage")]
public class TestClass
{
    //XmlElement not mandatory, since property names are the same
    [XmlElement(ElementName = "MessageId")]
    public string MessageId { get; set; }
}

The other pieces look good (Controller and header).

Michał Białecki created a very nice post about the topic. Please refer to it for more a more detailed implementation: Accept XML request in ASP.Net MVC Controller

Sign up to request clarification or add additional context in comments.

Comments

2

I was able to make it work. The only thing I had to change was the method Startup.ConfigureServices as follows:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
            .AddXmlSerializerFormatters(); 
}

5 Comments

Rui Jarumba, tried this now, getting error: Parse: Error parsing request: Root element is missing.
Seriously? I think my code and my request (using Postman) are exactly the same as yours. Let me double-check if there are any differences. Is the Content-Type the only header you added?
@Matt.G I can't find any differences, other than the ConfigureServices method. Controller method, XMLMessage data contract and the Postman request seems to be exactly like yours
@Matt.G Which versions of the Microsoft.AspNetCore.All and the Microsoft.NETCore.App nuget packages are you using? mine are v2.0.9 and v2.0.0
v2.0.8 and v2.0.0. tried upgrading to v.2.0.9, still getting same error
1

Startup.cs

using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace test
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(config =>
            {
                config.InputFormatters.Add(new XmlSerializerInputFormatter());
            }).AddXmlDataContractSerializerFormatters();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }
}

Controller.cs

using System.Runtime.Serialization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace test.Controllers
{
    [DataContract]
    public class TestClass
    {
        [DataMember]
        public string Message { get; set; }
    }

    [Route("[controller]")]
    public class TestController : Controller
    {
        [HttpPost, Route("test")]
        public async Task<IActionResult> Test([FromBody]TestClass test)
        {
            return Ok("OK");
        }

    }

}

Program.cs

using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting;

namespace test
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}

test.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.9" />
  </ItemGroup>
</Project>

7 Comments

Thanks for your answer. I had tried this earlier, with no success.
could you post your Startup.cs?
I have already added the relevant code from startup.cs in my post. Let me know if you need more.
you added new XmlDataContractSerializerInputFormatter() in the InputFormatters, and try new XmlSerializerInputFormatter()
I reproduced your issue when changing XmlSerializerInputFormatter to XmlDataContractSerializerInputFormatter()
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.