aspnetcore: ControllerBase.Ok(object value) responds with 204 status code when value is null
Describe the bug
Returning Ok(null) from a controller action results in a 204 status code, rather than a 200. I’m not sure if this in intended, but it’s not what I would expect.
To Reproduce
Steps to reproduce the behavior:
- Using this version of ASP.NET Core ‘2.2’
- Create a controller with the following method:
[HttpGet]
public async Task<IActionResult> Test(bool x)
{
if (x)
{
return Ok(null);
}
else
{
return Ok(new { });
}
}
- Make a HTTP Get request to the endpoint where x in the query string is true. The response status code will be 204.
- Make a HTTP Get request to the endpoint where x in the query string is false. The response status code will be 200 as expected.
Expected behavior
Response status code should always be 200 when returning Ok()
or Ok(object value)
, regardless of if value is null.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 11
- Comments: 24 (13 by maintainers)
This is especially confusing because an action method that returns
void
results in a 200 OK without any body. Not very consistent.This behaviour is done by
Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter
, the default configuration of this formatter will return 204 NO CONTENT if the body is null. To opt-out add this configuration in yourStartup.ConfigureServices
method:The behaviour is indeed confusing.
@syndicatedshannon In an API method that returns
IActionResult
, if you write code that is calling a method calledOk
, one expects that should return a status code of 200 OK in all cases by default. This seems like a reasonable expectation with no surprises. You as a developer are being explicit in requesting a specific status code. If you want to return 204, you may callNoContent()
, or you may opt in to a global configuration setting that returns 204 for anyOk
calls with no parameter or null. But to make that the default is surprising and not at all what the developer intends.That said, for
void
methods, as well asIActionResult<T>
where null is returned, I believe 204 is acceptable because you aren’t explicitly requesting a specific status code. But calling a method calledOk
is the developer saying “I want to return a 200 OK status code, even if the body is empty or it serializes tonull
”.I am not aware of any other status code methods that return status codes other than what they are named. i.e. does
NotFound()
ever return anything other than a 404? You wouldn’t expect it to. Edit: Assuming of course that no other middleware (i.e. error pages) rewrites the status code.I’m not sure how many people are relying on the behavior of
Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter
since its behavior is not obvious and unexpected.Is there a reason that the
HttpNoContentOutputFormatter.TreatNullValueAsNoContent
is true by default? Does this also affect other IActionResult implementations? Are there any projects that are known to depend on this behavior? This seems like something we should opt into if we want it, rather than having to opt out of it.I believe that this behavior should at least be disabled when using
Ok(object value)
, since Ok should guarantee a 200 status code (provided serialization is successful).Setting the status code to 204 when using
return Ok(null)
seems contradictory, especially whenreturn Ok()
is still a 200 with no content in the response body.I understand what
Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter
is trying to accomplish, but changing an Ok IActionResult to NoContent doesn’t seem to be intentional.If this is a big breaking change, then right now is the perfect time to do it since .net core is moving to its next major version.
@syndicatedshannon Based on the principle of least surprise, my expectation would be that it would be an empty body if you call
Ok()
with no parameter. Passingnull
would serialize it to the JSON stringnull
.Edit: in fact, that’s what the XML documentation says will happen for the
Ok
overload without a parameter:Wondering what it ‘should’ return.
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
The MDN doc is more opinionated, but if I follow it, then from void/Task-returning controller methods, I’d expect to return 204s. However, they don’t, and I don’t see an easy way to configure it.
The XML comment for
Ok(object value)
clearly states that 200 OK is expected:Worse, if you inspect the code for OkObjectResult, it appears like it should be using a 200 status code no matter what. That this behavior happens in the pipeline makes it difficult to diagnose. It also makes testing GET requests in the browser via the address bar difficult, as when 204 is returned, it doesn’t change pages.
Returning 204 No Content when the code explicitly is requesting a 200 OK response is confusing, unexpected, inconsistent with the documentation, and inconsistent with common wisdom that 204 is primarily for PUT/DELETE requests when in this case 204 would be returned even if the method is GET. I’m in favor of making this a breaking change to revert it to the expected behavior and not add the
HttpNoContentOutputFormatter
by default. Users may always add the formatter manually, use code likeif (value == null) return NoContent()
, and/or a newOkOrNoContent
method/result could be added.