Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Minimal API and XML formatters

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?

like image 531
AndrejT Avatar asked Aug 05 '26 23:08

AndrejT


1 Answers

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.

like image 113
Manuel Fuchs Avatar answered Aug 07 '26 12:08

Manuel Fuchs



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!