6

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?

AndrejT
  • 106
  • 2
  • 3
  • 5
    The short answer here is no. Minimal Web APIs [expect JSON](https://github.com/dotnet/aspnetcore/blob/release/6.0/src/Http/Http.Extensions/src/RequestDelegateFactory.cs#L580). – Kirk Larkin Nov 17 '21 at 15:14
  • 1
    Also you can try to look [here](https://stackoverflow.com/a/69867815/2501279) for some inspiration for ideas how to implement support in Minimal APIs. – Guru Stron Nov 17 '21 at 15:20

2 Answers2

2

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.

Manuel Fuchs
  • 399
  • 4
  • 12
0

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");
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 06 '22 at 00:16