skip to Main Content

Trying out minimal APIs in .NET 6 and can’t make it work with XML content type. If I use standard controllers, using .AddXmlSerializerFormatters() extension does the job:

builder.Services.AddControllers().AddXmlSerializerFormatters();

But when I switch from controller to .MapPost(..), I start getting 415 HTTP responses.

app.MapPost("/endpoint", ([FromBody] Request request) => {})
.Accepts<Request>("text/xml");

HTTP response: 415 Microsoft.AspNetCore.Http.BadHttpRequestException: Expected a
supported JSON media type but got "text/xml"

Is there any other way I can declare XML formatters that will work with minimal APIs?

2

Answers


  1. As suggested by the post linked by guru-stron, it’s possible to pass XML documents by implementing your own wrapping model that provides a BindAsync method.

    internal sealed class XDocumentModel
    {
        public XDocumentModel(XDocument document) => Document = document;
    
        public XDocument Document { get; init; }
    
        public static async ValueTask<XDocumentModel?> BindAsync(HttpContext context, ParameterInfo parameter)
        {
            if (!context.Request.HasXmlContentType())
                throw new BadHttpRequestException(
                    message: "Request content type was not a recognized Xml content type.",
                    StatusCodes.Status415UnsupportedMediaType);
    
            return new XDocumentModel(await XDocument.LoadAsync(context.Request.Body, LoadOptions.None, CancellationToken.None));
        }
    } 
    

    I added a extension method to HttpRequest for convenient Content-Type validation.

    internal static class HttpRequestXmlExtensions
    {
        public static bool HasXmlContentType(this HttpRequest request)
            => request.Headers.TryGetValue("Content-Type", out var contentType)
            && string.Equals(contentType, "application/xml", StringComparison.InvariantCulture);
    }
    

    You can then use the model directly as a paramter by your minimal API endpoint.

    app.MapGet("/xml-test", (XDocumentModel model) =>
    {
        // model.Document <- your passed xml Document
        return Results.Ok(new { Value = model.Document.ToString() });
    })
    

    Some final thoughts: This implementation enables you to pass a generic XML document to the endpoint. However, if you expect a certain document structure, you could implement this by making the XDocumentModel expect a generic type parameter and extracting this type’s properties from the XDocument instance.

    Login or Signup to reply.
  2. I did it this way:

    app.MapPost("/endpoint", (HttpContext c) => 
    {
        var reader = new StreamReader(c.Request.Body);
        var xml = reader.ReadToEndAsync().Result;
        // You can do with your xml string whatever you want
        return Results.Ok();
    }).Accepts<HttpRequest>("application/xml");
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search