zameb
zameb

Reputation: 850

Why I get Unsuported Media Type on GET request? (.NET 6)

I'm surely missing something, because most questions around 415 error are referring to POST requests. In this case, this is a very simple GET request, which works when I enumerate all action parameters, but if I create a simple DTO to contain all of these, I start receiving a 415 error.

I'm calling the api with https://localhost:555/some/test?field1=aaa&field2=1

This works:

    [ApiController]
    public class SomeController : ControllerBase
    {
        [Route("Test")]
        public SomeResponse GetSomeResponse(string field1, int field2)
        {
            return new SomeResponse(field1, field2);
        }
    }

But this doesn't:

    [ApiController]
    public class SomeController : ControllerBase
    {
        [Route("Test")]
        public SomeResponse GetSomeResponse(SomeRequest request)
        {
            return new SomeResponse(request.Field1, request.Field2);
        }
    }


    public class SomeRequest
    {
        public string Field1 { get; set; }
        public int Field2 { get; set; }
    }

    public class SomeResponse
    {
        public Someresponse(string field1, int field2)
        {
            Field1 = field1;
            Field2 = field2;
        }

        public string Field1 { get; set; }
        public int Field2 { get; set; }
    }

The controller class is only using Microsoft.AspNetCore.Mvc;

When I try to use SomeRequest class, the API answers "415 - Unsuported Media Type"

The only difference is the way to receive the values, I'm not switching from uri to body (which could be json or plain text, etc.) But since I'm not using the body, I can't understand which media-type it is referring to

My startup class is the same as the WeatherForecast, created with the project template ASP.NET Core Web API, with Visual Studio 2022, .Net6

Upvotes: 0

Views: 875

Answers (3)

Chris Schaller
Chris Schaller

Reputation: 16689

You should use the HttpGetAttribute to bind the request to query string parameters and specifically scope the requests so that only GET is allowed:

[HttpGet("Test")]
public SomeResponse GetSomeResponse2(SomeRequest request)
{
    return new SomeResponse(request.Field1, request.Field2);
}

It is subtle but since .Net 5 (when FromUriAttribute was replaced and the various OData and MVC routing mechanisms were combined into a common pipeline) we are encouraged to use HttpGetAttribute (Or the other Http Method variants) instead of RouteAttribute as a way of minimising the configuration.


This code has similar functionality to the answer provided by @Zameb but it prevents users from attempting to use POST to access this endpoint. [FromQuery] creates a controller that even when a JSON object is POSTed to the endpoint the parameters are specifically mapped to the Query Parameters.

Upvotes: 0

zameb
zameb

Reputation: 850

Well, a possible solution is to specify [FromQuery]:

public SomeResponse GetSomeResponse([FromQuery] SomeRequest request)

Tho, I am not very happy with this as reaction to "Unsuported Format", so, other suggestions are welcome.

Upvotes: 2

Chris Schaller
Chris Schaller

Reputation: 16689

Ben Foster has a great writeup on this in Custom Model Binding in ASP.NET 6.0 Minimal APIs

I haven't tried it myself yet but adding this TryParse method to your SomeRequest type might help:

static bool TryParse(string? value, IFormatProvider? provider, out T parameter)

Generally I try not to bind objects to HttpGet because standard object serialization uses characters that have other meanings in standard URL routing and therefore need to be escaped in the url, which just over complicates the server and the client implementation.

I support the concept for common complex type style structures like Point that might be reused thought you application in many controllers and endpoints, but to create an object wrapper for each request would be ludicrous, that is a common client side technique in generated clients but we try to avoid that on the server-side.

I recommend against binding objects as GET parameters as it is a non-standard style of code that introduces a level of technical debt to the solution both in terms of API management and client implementation.

Even if you use custom binders or route handlers these solutions end up being a lot more custom code than it would have been to use primitive parameters and map them to your preferred type implementation in the first line of your endpoint method.

Upvotes: 0

Related Questions