With the create/post method in place, we now turn to the R of CRUD, which is Read. This is depicted by the GET verb from http.
It is also the method that we referenced with the CreatedAtAction. For the GET verb we will introduce several methods in this section, but start with the simplest there is: the FindAll equivalent that will be called with the following URL

URL : localhost:5000/api/resource/

And this is how the corresponding controller method would look like:

public IEnumerable<Resource> Get()
    return _repository.FindAll();
  • For the GetAll we apply no routing attribute specification
  • Content negotiation should be applied with Produces/Consumes Attributes (is omitted for simplicities sake)
  • Postman looks like this, we assume we already created two resources with POST like on page 1

The other typical Read operation is the one by requesting a single Id. This can be done with the following URL and controller method.

URL: localhost:5000/api/resource/1

public IActionResult Get(int id)
    var resource = _repository.Find(id);
    if (resource == null)
        return NotFound();
        return Ok(resource);
  • Again we use model binding for the parameter, in this case one of int Id. It will be read directly from the URL and parsed into an int
  • The Route is specified by use of the id parameter
  • We return the status code of 404 NotFound if we cannot find the requested Resource in the collection. This is done by the NotFound() method.
  • If the resource is found we return it with the Ok method and the status code of 200. Because we did not specify content negotiation this will always be as JSON format. See the post on api controllers for content negotiation techniques.

The result with postman looks like this:

Update – or Put in http

Next method we need to implement is the Update method, which maps by industry standards to the PUT Verb of http.

To update a resource the convention is to use the idempotent PUT verb of http. The PUT request will in turn override the Resource at the server with the representation it delivers in its Request Body.

The method on the api controller would like the following snippet and work on the URL:

public IActionResult Put([FromBody] Resource resource)
     if (!ModelState.IsValid)
         return BadRequest();

     var existingResource = _repository.Find(resource.Id);
     if (existingResource == null)
          return NotFound();

     existingResource.Name = resource.Name;

     return NoContent();
  • As with the POST request the ModelState is validated, and if the state is not valid a BadRequest with status code 406 for not accepted is sent as a response.
  • Also if the representation that is encoded in the body of the request is not found at the server a NotFoundResult with 404 is sent as a response.
  • If all is well you could either return a 200 status code for Ok, a 202 Accepted and further processing or like I did a 204 NoContent response.

Again the Postman request would look like this (note again the representation that is simply added as application/json format to the body of the request):

On a side note PUT can also be used to create resources in an idempotent way. This are considerations for the API as a whole that can make sense, but exceed the limits of what I want to show in this post.


Last but not least we have D of CRUD with Delete. This maps to the http Verb of DELETE and can be implemented as follows with Asp Net Core Web API:

public IActionResult Delete(int id)
     var resource = _repository.Find(id);
     if (resource == null)
          return NotFound();


     return NoContent();
  • The delete method will be returning an IActionResult to cope with the style we found in the other described methods. Its input is an integer id that is supplied again by model binding.
  • If the model binding Id cannot be found in the repository we return a NotFoundResult. Else the repository.Delete method is called with the resource as parameter.
  • If one would invoke the delete method without an Id (http://localhost:5000/api/resource/) then a BadRequestResult is returned implicitly from model binding.

If all is well, the method simply returns a NoContentResult. Other appropriate status codes would be 200 or 202 (see also PUT).

Also we have a postman impression for this method (we assume we created a third resource with a POST request just to immediately delete it afterwards:



In this post we saw how to create a simple CRUD like controller with ASP net Core Web Api. We looked at how the http Verbs can be matched to CRUD style operations and how restful semantics can be applied to an api controller.

Create matches to POST, Read is equivalent to GET, PUT resembles Update and DELETE appropriately matches Delete. This is by no means a complete way to a restful controller, yet it is a good starting point for a web api.

This is by no means a comprehensive look on Restful controllers. Some further topics are unit testing your controller, validation for application logic, entity framework and connecting to a database and so on and so forth.

Further read
– Intro to controllers
– REST (Webservices category)
– Intro to api controllers
– unit test your controllers
– test your api with postman/powershell/curl

Categories: HowTo


Leave a Reply