Search code examples
c#.netasp.net-coreasp.net-web-apijwt

UnsupportedContentTypeException appears to occur due to application/jwt in Post


I'm using .NET 6.0, and have a simple API Controller to receive a POST from a 3rd party website. They are, however, posting with the Content-Type application/jwt, and according to the trace logs I'm getting a "UnsupportedContentTypeFilter" exception.

I can post code if needed, but I get similar results if I use postman and set Content-Type to application/jwt.

Example to illustrate:

public class GlassfyController : ControllerBase
{
    [HttpPost("ReceiveWebHook")]
    public async Task<ActionResult> ReceiveWebHook([FromBody]GlassfyWebHookRequest req)
    {
       return Ok();
    }
}

If I post to this with application/json from postman, it works fine. However, if I have Glassfy post to it, it returns HTTP 415.

I noticed by looking at logs that it's coming in as application/jwt from Glassfy. I can't really control that, is there some way to get application/jwt to be recognized by the model binder?

The exception is:

Rejected input formatter 'Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter' for content type 'application/jwt'.

According to Microsoft's documentation it says:

UnsupportedContentTypeException The Exception that is added to model state when a model binder for the body of the request is unable to understand the request content type header.

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.unsupportedcontenttypeexception?view=aspnetcore-8.0

Any help would be greatly appreciated! By the way it hasn't eluded me as to what a JWT has to do with a POST request, except that they're supposed to just be adding a JWT token as a Bearer (by running it through IIS and pulling failed request trace logs, I can see it).. and providing a URL to validate it. I can't even get the call to land due to the 415. It's also suspicious because when I pull the token from Authorization Bearer, and throw it into a JWT decoder, I only get the first two parts, it's missing the third section completely. I must have a fundamental misunderstanding. The core of my question though, is how to get .net core to accept Content-Type: application/jwt since apparently I need to receive that.


Solution

  • you can Handle a Content-Type like application/jwt in ASP.NET Core, which is not natively supported by the model binding infrastructure, it requires a custom approach. Since application/jwt is a specialized content type typically used for JWTs (JSON Web Tokens), so you'll need to handle it explicitly in your controller or you can do it by creating a custom input formatter.

    let assume the follwing scenario as you've described, the model binder is rejecting the content type because it's set up to handle application/json by default, not application/jwt. To work around this, you have a couple of options: First Custom Middleware to Change the Content-Type One approach is to intercept the request before it reaches your action and modify the Content-Type header to application/json if it's application/jwt. This way, the model binding process proceeds as usual, assuming the body content is JSON.

    Here is a simplified example of such middleware:

    public class ContentTypeMiddleware
    {
        private readonly RequestDelegate _next;
    
        public ContentTypeMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            if (context.Request.ContentType == "application/jwt")
            {
                context.Request.ContentType = "application/json";
            }
    
            await _next(context);
        }
    }
    

    Then Register this middleware in your Startup.cs or equivalent setup file before the MVC middleware:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // Other configurations...
    
        app.UseMiddleware<ContentTypeMiddleware>();
    
        // MVC or endpoint routing configurations...
    }
    

    secound option Custom Input Formatter you use Another approach is to create a custom input formatter that can handle application/jwt. This involves more work but is a cleaner solution, especially if the JWT payload needs special processing.

    Here's a basic structure for a custom input formatter:

    public class JwtInputFormatter : TextInputFormatter
    {
        public JwtInputFormatter()
        {
            SupportedMediaTypes.Add("application/jwt");
            SupportedEncodings.Add(Encoding.UTF8);
            SupportedEncodings.Add(Encoding.Unicode);
        }
    
        public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
        {
            var request = context.HttpContext.Request;
            using var reader = new StreamReader(request.Body, encoding);
            var content = await reader.ReadToEndAsync();
            // Assuming the JWT payload is a simple JSON, decode or process it as needed
            var model = JsonConvert.DeserializeObject<GlassfyWebHookRequest>(content); // Use appropriate deserialization
            return await InputFormatterResult.SuccessAsync(model);
        }
    }
    

    Then you should register this input formatter in your MVC setup in Startup.cs class

    services.AddControllers(options =>
    {
        options.InputFormatters.Insert(0, new JwtInputFormatter());
    });
    

    By this way, when a request with Content-Type: application/jwt is received, your custom input formatter will process it. You might need to adjust the deserialization logic depending on the actual content and structure of the JWT payload.

    Both of above methods allow you to work around the limitation without needing to control the Content-Type of the incoming requests. you can try both of above and choose the one that best fits your application's architecture and your specific needs.