2
    [HttpPost]
    [Route("Patient/$gpc.registerstudent")]       
    [SwaggerOperation(Summary = "Register student")]
    public async Task<IActionResult> Registerstudent([FromBody] JObject parameters,[FromBody] xElement Param )
    {
    }

Nothing worked. I am using ASP .NET Web API. And my requirement is that should be able to post me XML as well as JSON requests.currently am able to request only json request

xml example:

Anil patil
  • 31
  • 7
  • "Nothing worked" what did you try? Did you use `AddXmlSerializerFormatters` – Charlieface Jul 04 '23 at 11:49
  • i have added in startup.cs services.AddControllers().AddXmlSerializerFormatters() tried but not worked – Anil patil Jul 04 '23 at 11:53
  • Also `[FromBody] JObject parameters` what do you expect to happen here? You would have to pass a JObject inside XML? And you can't have two `FromBody` parameters anyway. Perhaps you should just have a single `[FromBody] dynamic parameters` although to be honest it's not clear why you don't have explicit paramaters rather than some big dynamic blob. – Charlieface Jul 04 '23 at 12:03
  • Student/$gpc.registerstudent?_format=application%2Ffhir%2Bxml this my RequestUri now am getting Bad Request for XML Post json is working fine. – Anil patil Jul 04 '23 at 12:21
  • That's not how you pass a format option. You need to pass it in the `Accept` header. – Charlieface Jul 04 '23 at 12:35
  • I am not sure why `Nothing worked`, But the solution is add `AddXmlSerializerFormatters` in your configuration. – Xinran Shen Jul 05 '23 at 03:15
  • added all configuration still am getting bad request for XML Request Body – Anil patil Jul 05 '23 at 04:53

1 Answers1

0

If you want to support multiple de-serializers for the same controller endpoint, then you should not try to accept a generic content like JObject or xElement as the argument for your controller. The other Faux Pas is that your method can only have a single argument decorated with the FromBodyAttribute.

Configuring services.AddControllers().AddXmlSerializerFormatters() is all you need to support automatic deserialization from XML and Serialization to XML via a mechanims called content negotiation. For this to work as an input type, you must first use a typed argument in your method, then the FromBodyAttribute will use the configured SerializerFormatters to deserialize the body to the requested type based on the Content-Type of the request.

One caveat that I have found is that you should use the application/xml as the Content-Type for posting and XML document.

We use the Consumes attribute to declare the supported input content types, the Produces attribute describes the response types that are supported.

[HttpPost]
[Route("Patient/$gpc.registerstudent")]       
[SwaggerOperation(Summary = "Register student")]
[Consumes("application/xml", "application/json")]
[Produces("application/xml", "application/json")]
public async Task<IActionResult> Registerstudent([FromBody] RegisterStudentRequestDTO request)
{
    await Task.CompletedTask;
    return Ok(request); // just echo out for now to show the output serialization
}

In this case my register DTO is very simple:

public class RegisterStudentRequestDTO
{
    public string Name { get; set; }
    public DateTime Date { get; set; }
    public string Comments { get; set;}
}

The way that a caller can define the expected response type if by providing an Accept header. For instance, we can now provide a XML input to this method and tell the controller to serialize the response into XML:

In the request, Content-Type header describes the input that we are providing, the Accept header describes the content-type of the response.

curl -X 'POST' \
  'https://localhost:7208/WeatherForecast/Patient/$gpc.registerstudent' \
  -H 'accept: application/xml' \
  -H 'Content-Type: application/xml' \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<RegisterStudentRequestDTO>
    <Name>string</Name>
    <Date>2023-07-04T12:34:43.128Z</Date>
    <Comments>string</Comments>
</RegisterStudentRequestDTO>'

You will notice that the name of the elements MUST match the casing of the name of the class and properties, by default the XML serializer is case-sensitive.

This will return:

<RegisterStudentRequestDTO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Name>string</Name>
    <Date>2023-07-04T12:34:43.128Z</Date>
    <Comments>string</Comments>
</RegisterStudentRequestDTO>

But if you change the Accept header to application/json then without altering any code, the output changes to this:

{
    "name": "string",
    "date": "2023-07-04T12:29:14.595Z",
    "comments": "string"
}

To complete the demo, to provide JSON input, the request would look like this:

curl -X 'POST' \
  'https://localhost:7208/WeatherForecast/Patient/$gpc.registerstudent' \
  -H 'accept: application/xml' \
  -H 'Content-Type: application/json' \
  -d '{ "name":"string", "date":"2023-07-04T12:29:14.595Z" , "comments": "string" }'

The response will still be in application/xml due to the Accept header.

Further Reading:

You can also read about a workaround here: Issue receiveing POST request with Content-Type text/xml


If you receive a response similar to this:

<problem xmlns="urn:ietf:rfc:7807">
    <status>400</status>
    <title>One or more validation errors occurred.</title>
    <type>https://tools.ietf.org/html/rfc7231#section-6.5.1</type>
    <traceId>00-94549daf9ab55604a516f0b2b012d563-23030c613b93e726-00</traceId>
    <MVC-Errors>
        <MVC-Empty>An error occurred while deserializing input data.</MVC-Empty>
        <request>The request field is required.</request>
    </MVC-Errors>
</problem>

Then please check the casing of your class or property names, the swagger document will use (lower) camel case by default in the Try it Now section, this is unfortunate:

try it now example

Providing the correctly typed input will work, you can also implement DataContractAttribute and DataMemberAttribute to explicitly rename the properties to their lower case variant. If you do this, you will need to also add the .AddXmlDataContractSerializerFormatters() to your fluent AddControllers() implementation in startup.cs/program.cs

Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
  • hi Need More Information on above – Anil patil Jul 06 '23 at 08:02
  • after making above changes also getting An error occurred while deserializing input data.The request field is required. – Anil patil Jul 06 '23 at 09:41
  • I tried to explain that at the end, if you get "Field is required" then this probably means your casing does not match _exactly_ with the class and property names. You should update your post with the _exact_ class definition and the body content that you are sending. – Chris Schaller Jul 06 '23 at 16:15
  • thanks normal content type is working fine.how to post fhir+xml to web api? – Anil patil Jul 07 '23 at 06:10
  • It should work fine as long as the input deserializes into your DTO class correctly. The error indicates that there is an issue with the structure of your class. You would be better off using JSON output instead of fhir+xml if you can, otherwise you should edit your post with an example input and your DTO class (as I asked earlier) then I can show you specifically where the issue is. – Chris Schaller Jul 07 '23 at 15:43
  • hi @Chris Schaller Thanks for your input. from consumer we are getting xml and json we are just posting same to web api we are unable to post xml as input. – Anil patil Jul 08 '23 at 02:29
  • in api we input [FromBody] xElement then its working fine and json failing...we need to accept both xml and json. – Anil patil Jul 08 '23 at 02:34
  • how to post both xml and json to Endpoint.we have tried dynamic,string not working – Anil patil Jul 08 '23 at 02:36
  • The solution that I have presented, where you use a _DTO_ and `AddXmlSerializerFormatters` is how you support both types of inputs with a single method. Using `xElement` or `JObject` will restrict it to XML or JSON generic input (untyped) but not both. Your problem appears to be in your DTO definition, but since you have not posted the raw input or a typed class to deserialize the response to, it is hard to give you a specific answer to your current issue. – Chris Schaller Jul 09 '23 at 16:39