ASP.NET core - how to pass optional [FromBody] parameter?
Asked Answered
H

6

18

How do I pass optional (nullable) [FromBody] parameter in ASP.NET Core (5.0)? If I don't send body in my request I get 415 Unsupported Media Type error. Can this be configured and if so, how to do it on a controller or action, rather than an app level? I presume it has to do something with model validation, but not sure. Thanks.

[HttpGet("[action]")]
public async Task<IActionResult> GetElementsAsync([FromBody] IEnumerable<int> elements = default)
{
  var result = await dataService.GetData(elements);
  return Ok(result);
}

EDIT: To clarify:

This is typical scenario and it works normally: This is typical scenario and it works normally

But passing empty body is returning 415 right away without even reaching action: Passing empty body is returning 415 right away without reaching action

Hey answered 13/9, 2021 at 8:30 Comment(1)
In a project targeting .NET 6.0, I was able to pass a null string parameter in the request by using string? instead of string in the request model. A validation error was thrown if the ? wasn't included.Interoffice
A
11

Just add content-type in your request header. Without the content-type:application/json will appear 415 when body is empty.

No changes to your controller. Test in my side is ok.

enter image description here

I created a new asp.net core 5 api project and this is my controller:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace WebApi_net5.Controllers
{
    public class HomeController : ControllerBase
    {
        [HttpGet("[action]")]
        public string GetElementsAsync([FromBody] IEnumerable<int> elements = default)
        {
            return "value";
        }
    }
}
Afoot answered 14/9, 2021 at 3:14 Comment(3)
ASP.NET Core team fixed this in .NET 7.0 so setting 'content-type' should no longer be needed. github.com/dotnet/aspnetcore/pull/38092Hey
thanks for your back and inform me about it : )Afoot
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] is also needed for .NET 6.0Nowadays
G
14

You can find a solution here:
https://github.com/pranavkm/OptionalBodyBinding

From this issue on github:
https://github.com/dotnet/aspnetcore/issues/6878

And from .net Core 5 you can use this one:

public async Task<IActionResult> GetElementsAsync([FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] IEnumerable<int> elements = default)
...

Also needed (from Pawel experience):

services.AddControllers(options =>{options.AllowEmptyInputInBodyModelBinding = true;})
Gantz answered 13/9, 2021 at 8:45 Comment(5)
Does this solution still requires creating custom filter? I'm on .NET core 5.0 and just adding (EmptyBodyBehavior = EmptyBodyBehavior.Allow) did not change behaviour.Hey
Should be available without custom filter. Chech the global setting. Doc in GitHub and ms docGantz
I tried adding nullable reference type annotation (IEnumerable<int>? - someone claimed this worked for them: github.com/dotnet/aspnetcore/issues/30690), configure services with services.AddControllers(options =>{options.AllowEmptyInputInBodyModelBinding = true;}) and disabling model validation with services.Configure<ApiBehaviorOptions>(options => {options.SuppressModelStateInvalidFilter = true;}); None of this worked :/Hey
Great job Pawel !Gantz
I think [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] expects user to send empty body (as the name would suggest) and not just not sending any body. Difference between message with no body vs message with empty body is like so: curl --location --request POST 'https://localhost:44349/MyController/MyAction' vs curl --location --request POST 'https://localhost:44349/MyController/MyAction' \ --data-raw ''Hey
A
11

Just add content-type in your request header. Without the content-type:application/json will appear 415 when body is empty.

No changes to your controller. Test in my side is ok.

enter image description here

I created a new asp.net core 5 api project and this is my controller:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace WebApi_net5.Controllers
{
    public class HomeController : ControllerBase
    {
        [HttpGet("[action]")]
        public string GetElementsAsync([FromBody] IEnumerable<int> elements = default)
        {
            return "value";
        }
    }
}
Afoot answered 14/9, 2021 at 3:14 Comment(3)
ASP.NET Core team fixed this in .NET 7.0 so setting 'content-type' should no longer be needed. github.com/dotnet/aspnetcore/pull/38092Hey
thanks for your back and inform me about it : )Afoot
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] is also needed for .NET 6.0Nowadays
S
2

With ASP.NET Core 3.1, I could allow nullable optional parameters by implementing Nicola's suggestion:

services.AddControllers(options =>{options.AllowEmptyInputInBodyModelBinding = true;})
Servant answered 22/2, 2022 at 9:52 Comment(0)
C
1

I will address some points that were not mentioned here.

  1. To get rid of 415 without sending Content-Type you need to create your custom consumer of the Content-Type

https://mcmap.net/q/740682/-unable-to-get-asp-net-web-api-to-consume-an-unspecified-content-type

But I would say this is an overkill

  1. If you pass Content-Type: application/json in your request header you will get "Body cannot be empty" (Tested on .NET 6) and only then @Nicola answer comes in handy.

From my tests it looks like modifications to the controller are not needed and only FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow) is enough with nullable type as parameter.

  1. Or you can change nothing in your current code and send a header Content-Type: application/json with a body of {}. This will bypasses all of those errors, but this is not the most optimal solutions for public API's
Colossus answered 3/8, 2022 at 21:15 Comment(0)
T
0

As per filters execution flow diagram https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.1 ResourceFilters are executed before model binding so you can make a hack and set content-type in order model binder to not crash so you can accept request with no body and content-type header passed from client

public class MediaTypeResourceFilterAttribute : Attribute, IResourceFilter
{
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
           context.HttpContext.Request.ContentType ??= "application/json";
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }
}
Templeton answered 14/4, 2023 at 10:46 Comment(0)
D
0

add this line to configuration

services.AddControllers().ConfigureApiBehaviorOptions(options => { options.SuppressModelStateInvalidFilter = true; });

automatic-http-400-responses

Dobrinsky answered 3/4, 2024 at 11:26 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.