When I’m designing a RESTful API, I like to use the correct verbs for the correct actions, and return HTTP status codes that convey the correct message. Sure, its easy to just return a 200 OK for everything, but its a little more useful to return 204 No Content if your client shouldn’t navigate away or expect any data back from a call.
IActionResult
IActionResult is nice because it lets you return specific status codes easily with (or without) data attached:
public class MyController: Controller | |
{ | |
public MyController(IComplexTypeService complexTypeService) | |
{ | |
_complexTypeService = complexTypeService; | |
} | |
[HttpPost("{id}")] | |
public async Task<IActionResult> AddComplexType(string id, [FromBody]ComplexType cType) | |
{ | |
if (cType == null) | |
{ | |
// Bad parameter! The request is invalid | |
return BadRequest(); | |
} | |
if (!await _complexTypeService.Exists(id)) | |
{ | |
// The id does not exist! | |
return NotFound(); | |
} | |
if (!await _complexTypeService.Add(id, cType)) | |
{ | |
// Something went wrong adding the complex type! | |
return BadRequest(); | |
} | |
// Everything went well! | |
return NoContent(); | |
} | |
private readonly IComplexTypeService _complexTypeService; | |
} |
As you can see in the above, there’s a bunch of built in functions that map to HttpStatus codes that you can use when returning an IActionResult.
Some of the built in functions:
Ok –> 200 Ok
NoContent –> 204 No Content
BadRequest –> 400 Bad Request
NotFound –> 404 Not Found
These get you off to a great start, but I was kind of disappointed that InternalServerError was missing. If something went goofy and I can’t really explain why, sometimes I would prefer that the controller return an internal server error so the client knows that it might be safe to try their request again.
Luckily, its pretty easy to add:
public static class ControllerExtensions | |
{ | |
public static InternalServerErrorResult InternalServerError(this Controller controller) | |
{ | |
return new InternalServerErrorResult(); | |
} | |
} |
/// A <see cref="StatusCodeResult"/> that when | |
/// executed will produce an Internal Server Error (500) response | |
/// </summary> | |
public class InternalServerErrorResult : StatusCodeResult | |
{ | |
/// <summary> | |
/// Creates a new <see cref="InternalServerErrorResult"/> instance. | |
/// </summary> | |
public InternalServerErrorResult() : base(StatusCodes.Status500InternalServerError) | |
{ | |
} | |
} |
using ControllerExtensions; | |
public class MyController: Controller | |
{ | |
[HttpGet("InternalServerError")] | |
public async Task<IActionResult> InternalServerError() | |
{ | |
// Preface with "this", because we defined it as an extension | |
// method for a Controller class | |
return this.InternalServerError(); | |
} | |
} |
Just create whichever type of request you want by inheriting a StatusCodeResult.
Then, create an extension class and extend Controller to have an implementation of your new class.
To invoke it, preface the call with this (since its an extension method, you need this for context).