title: Returning a 404 from an explicitly typed ASP.NET Core API controller (not IActionResult)
body: <p>ASP.NET Core API controllers typically return explicit types (and do so by default if you create a new project), something like:</p>
  
  <pre><code>[Route("api/[controller]")]
  public class ThingsController : Controller
  {
      // GET api/things
      [HttpGet]
      public async Task&lt;IEnumerable&lt;Thing&gt;&gt; GetAsync()
      {
          //...
      }
  
      // GET api/things/5
      [HttpGet("{id}")]
      public async Task&lt;Thing&gt; GetAsync(int id)
      {
          Thing thingFromDB = await GetThingFromDBAsync();
          if(thingFromDB == null)
              return null; // This returns HTTP 204
  
          // Process thingFromDB, blah blah blah
          return thing;
      }
  
      // POST api/things
      [HttpPost]
      public void Post([FromBody]Thing thing)
      {
          //..
      }
  
      //... and so on...
  }
  </code></pre>
  
  <p>The problem is that <code>return null;</code> - it returns an HTTP <code>204</code>: success, no content.</p>
  
  <p>This is then regarded by a lot of client side Javascript components as success, so there's code like:</p>
  
  <pre><code>const response = await fetch('.../api/things/5', {method: 'GET' ...});
  if(response.ok)
      return await response.json(); // Error, no content!
  </code></pre>
  
  <p>A search online (such as <a href="https://stackoverflow.com/questions/37793418/how-to-return-http-500-from-asp-net-core-rc2-web-api">this question</a> and <a href="https://stackoverflow.com/a/41465343">this answer</a>) points to helpful <code>return NotFound();</code> extension methods for the controller, but all these return <code>IActionResult</code>, which isn't compatible with my <code>Task&lt;Thing&gt;</code> return type. That design pattern looks like this:</p>
  
  <pre><code>// GET api/things/5
  [HttpGet("{id}")]
  public async Task&lt;IActionResult&gt; GetAsync(int id)
  {
      var thingFromDB = await GetThingFromDBAsync();
      if (thingFromDB == null)
          return NotFound();
  
      // Process thingFromDB, blah blah blah
      return Ok(thing);
  }
  </code></pre>
  
  <p>That works, but to use it the return type of <code>GetAsync</code> must be changed to <code>Task&lt;IActionResult&gt;</code> - the explicit typing is lost, and either all the return types on the controller have to change (i.e. not use explicit typing at all) or there will be a mix where some actions deal with explicit types while others. In addition unit tests now need to make assumptions about the serialisation and explicitly deserialise the content of the <code>IActionResult</code> where before they had a concrete type.</p>
  
  <p>There are loads of ways around this, but it appears to be a confusing mishmash that could easily be designed out, so the real question is: <strong>what is the correct way intended by the ASP.NET Core designers?</strong></p>
  
  <p>It seems that the possible options are:</p>
  
  <ol>
  <li>Have a weird (messy to test) mix of explicit types and <code>IActionResult</code> depending on expected type.</li>
  <li>Forget about explicit types, they're not really supported by Core MVC, <em>always</em> use <code>IActionResult</code> (in which case why are they present at all?)</li>
  <li>Write an implementation of <code>HttpResponseException</code> and use it like <code>ArgumentOutOfRangeException</code> (see <a href="https://stackoverflow.com/a/41484262/905">this answer</a> for an implementation). However, that does require using exceptions for program flow, which is generally a bad idea and also <a href="https://github.com/aspnet/Mvc/issues/4311#issuecomment-198324107" rel="noreferrer">deprecated by the MVC Core team</a>.</li>
  <li>Write an implementation of <code>HttpNoContentOutputFormatter</code> that returns <code>404</code> for GET requests.</li>
  <li>Something else I'm missing in how Core MVC is supposed to work?</li>
  <li>Or is there a reason why <code>204</code> is correct and <code>404</code> wrong for a failed GET request?</li>
  </ol>
  
  <p>These all involve compromises and refactoring that lose something or add what seems to be unnecessary complexity at odds with the design of MVC Core. Which compromise is the correct one and why?</p>