Conventional Routing doesn't deserialize json body requests (ASP.NET Core API)
Asked Answered
W

1

1

Problem

Using ApiControllerAttribute and RouteAttribute on controllers and actions, everythings work fine.

When I change the code to work with Convetional Routing, the Identity property in request is always set to null.

Code with ApiControllerAttribute (Identity loaded in request)

[ApiController]
[Route("api/[controller]")]
Public Class Main : ControllerBase
{
    [HttpPost(nameof(GetExternalRemoteExternal))]
    public async Task<GetByIdentityResponse<RemoteExternal>> GetExternalRemoteExternal(GetByIdentityRequest<RemoteExternalIdentity> request)
    {
        return await GetExternal<RemoteExternal, RemoteExternalIdentity>(request);
    }
}

startup.cs

app.UseEndpoints(endpoints => endpoints.MapControllers());

Code with Convetional Routing (request has null Identity)

Public Class Main : ControllerBase
{
    [HttpPost]
    public async Task<GetByIdentityResponse<RemoteExternal>> GetExternalRemoteExternal(GetByIdentityRequest<RemoteExternalIdentity> request)
    {
        return await GetExternal<RemoteExternal, RemoteExternalIdentity>(request);
    }
}

startup.cs

app.UseEndpoints(endpoints => endpoints.MapControllerRoute(
                                               name: "default",
                                               pattern: "api/{controller}/{action}")) //Not work even with "api/{controller}/{action}/{?id}"

Common code

public class GetByIdentityRequest<TIDentity> : ServiceRequest
    where TIDentity : BaseIdentity
{
    public TIDentity Identity { get; set; }
}

public class RemoteExternalIdentity : BaseIdentity
{
    public int IdX { get; set; }
}

JSON

{"$id":"1","Identity":{"$id":"2","IdX":10000}}

API LINK

.../api/Main/GetExternalRemoteExternal

Woodpecker answered 18/2, 2020 at 11:2 Comment(2)
Can you show the annotations of your controller? Also, what URLs are you using to make the requests?Mcbroom
Try putting a [FromBody] before the parameter type GetByIdentityRequest<RemoteExternalIdentity>. The [ApiController] attribute adds a few conventions which may cause the difference here.Mcbroom
M
2

The [ApiController] attribute adds a few conventions to controllers that enables some opinionated behaviors, including binding source parameter inference that will make complex parameters bind from the body by default.

Since you cannot use the [ApiController] attribute with convention-based routing (since one of the convention is to prevent exactly that), you can use an explicit [FromBody] with your parameters to force them to be parsed from the JSON body:

public class Main : ControllerBase
{
    [HttpPost]
    public async Task<GetByIdentityResponse<RemoteExternal>> GetExternalRemoteExternal(
        [FromBody] GetByIdentityRequest<RemoteExternalIdentity> request)
    {
        return await GetExternal<RemoteExternal, RemoteExternalIdentity>(request);
    }
}
Mcbroom answered 19/2, 2020 at 9:46 Comment(1)
Thank you so much for the explanation. I scratched my head for about 1 hour before I looked up the solution and found your solution. (Y)Berkowitz

© 2022 - 2024 — McMap. All rights reserved.