Search code examples
c#asp.net-core.net-coreasp.net-core-3.1

Asp Core 3 - How to allow nullable [FromBody] object in controller method


Let's say I have a simple controller with one POST method that accepts an object from its body. However, the presence of this object should be optional in the HTTP request body. I tried to implement this behavior with the following code

public class User
{
    public string Name { get; set; }
}

[ApiController]
[Route("[controller]")]
public class GreetingController : ControllerBase
{
    [HttpPost]
    public string SayHello([FromBody] User user = null)
    {
        return "Hello " + user?.Name;
    }
}

If I make a request with an object in the body, everything works fine. But with this configuration, it is unable to make a POST request with empty body. If I create a request without Content-Type header (as there is actually no content), I get the following error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
    "title": "Unsupported Media Type",
    "status": 415,
    "traceId": "|192e45d5-4bc216316f8d3966."
}

if the Content-Type header has value application/json then the response looks like this:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|192e45d6-4bc216316f8d3966.",
    "errors": {
        "": [
            "A non-empty request body is required."
        ]
    }
}

So, how to make the object optional in the request body? It is quite common problem and I am curious if there is a simple solution for that in ASP Core 3. I don't want to read the object from Request stream and deserialize it on my own.


Solution

  • There is now an easier way (since 5.0-preview7)

    You can now achieve this per action method, by configuring a FromBodyAttribute property named EmptyBodyBehavior

    Demonstration:

    public IActionResult Post([FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] MyModel model)
    

    Thanks to LouraQ's comment, which guided me towards the above answer on github