ASP.NET Core 6 - Use URI's extension to get Accept header value

Viewed 22

I'm migrating an application from NancyFx to Kestrel in ASP.NET Core 6.

In Nancy, you could specify the Accept value in the URI. For example, these Uris:

  • http://localhost:5000/my/resource.json
  • http://localhost:5000/my/resource.protobuf
  • http://localhost:5000/my/resource.xml

Would be the equivalent of setting the Accepts header to application/json, application/protobuf or application/xml respectively.

Does this exist in Kestrel? I remember finding one example, long ago, of regex-ing the route and doing it somewhat manually. But

  1. I can't find that post again, and
  2. If I have to do that, I'm not sure I want to :)

Is there a way to configure this behavior in ASP.NET Core 6?

The object returned from my handler in the controller is already capable of being serialized to json/xml/whatever. I just need to check the URI to set the content-type of the response so the correct formatter will be invoked.

At the moment, I have a client that will speak to both Nancy and Kestrel and it was written to use the URI to get the type. I'm fine to rewrite/update the client so it will use the Accept header. But getting the URI method to work will make the initial integration easier and a refactor to use the headers can come next.

2 Answers

I created a very simple middleware that reads the accept value from the query string and sets the Accept header to the request:

public class AcceptHeaderFromQueryString
{
    private readonly RequestDelegate _next;

    public AcceptHeaderFromQueryString(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var accept = context.Request.Query["accept"];

        if (!string.IsNullOrEmpty(accept))
        {
            context.Request.Headers.Accept = accept;
        }

        await _next(context);
    }
}

Register the middleware:

app.UseMiddleware<AcceptHeaderFromQueryString>();

I added [Produces(MediaTypeNames.Application.Json, MediaTypeNames.Application.Xml)] attribute to my api controller action (this step is not required):

[HttpGet]
[Produces(MediaTypeNames.Application.Json, MediaTypeNames.Application.Xml)]
public IEnumerable<WeatherForecast> Get()
{
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray();
}

Finally I added support for xml serialization in Program.cs:

builder.Services.AddControllers()
    .AddXmlDataContractSerializerFormatters();

Then I tried these urls and they both gave appropriate response:

https://localhost:7258/weatherforecast?accept=application/json

https://localhost:7258/weatherforecast?accept=application/xml

You possibly want the [Consumes] attribute. This allows you to specify a controller action that only gets called from a route of the specified content type.

Obviously this is not using the Accepts header but the content type of the request.

Related