ASP.NET Core 2.2: Creating Resources with HTTP POST

We are going to use the HttpPost attribute to decorate an action that will create a resource in a Web API application in ASP.NET Core 2.2.

When we create a resource, the correct thing is for our Web API to return a status code 201 Created. When we do this, we must place in the Location header of the HTTP response the URL of the new resource created, and, in addition, in the body of the response, we must place the new resource. This may sound like there are many things to do to give a correct response to an HTTP POST, however, ASP.NET Core makes it easy for us, we just have to use a class called CreatedAtRouteResult and this will take care of creating the correct answer from our Web API.

Naming a Routing Rule

One issue that we must touch is that we can give names to routing rules. The idea of naming a routing rule is to be able to reference that routing rule from anywhere in our Web API.

To name a routing rule, we use the Name parameter as follows:

[HttpGet("{id}", Name = "GetAuthor")]
public async Task<ActionResult<AuthorDTO>> Get(int id)
{…}

Then, the name of the previous routing rule is GetAuthor. We are going to use this to be able to make our example of creating a resource.

Creating the Resource

For that, we go to the AuthorsController controller and create the following method:

[HttpPost]
public async Task<ActionResult> Post([FromBody] Author author)
{
  _context.Authors.Add(author);
  await _context.SaveChangesAsync();
  var authorDTO = _mapper.Map<AuthorDTO>(author);
  return new CreatedAtRouteResult("GetAuthor", new { id = author.Id }, authorDTO);
}

Notice that we are using async to take advantage of asynchronous programming in our Web API. When we are going to perform operations with the database, it is good practice to use asynchronous programming, since it allows us to manage the resources of our web server in a more effective way.

In the Post function parameter, we place the FromBody attribute, indicating that the author’s information will come in the body of the HTTP request.

At the end of the Post function, we are using the CreatedAtRouteResult class to return a 201 Created. Notice that we have placed the name of the route where the resource is located to be consulted individually. We also pass the author’s DTO to that class, so that it is placed in the body of the HTTP response.

(We talked about DTOs in a previous blog post)

With respect to returning a DTO in the body of our HTTP response, I do so to match the return data type of the method Get(int Id), since that method returns an AuthorDTO. Certainly, we could have returned an Author in this case, because in itself, the input data type of the Post function is an author. Which brings me to the next point, due to issues of separation of responsibilities, some people choose to create a DTO for the creation of resources, which would be different from the DTO of data return. This makes it impossible for a client to try to send you a field, such as the Id, populated, when it is assumed that the Id field is self-generated by us. If we used this strategy, we would do the following.

First, we created a class called AuthorCreationDTO:

public class AuthorCreationDTO{
  public string Name { get; set; }
  public DateTime Birthdate {get; set;}
}

In this class we have placed only the fields relevant to the creation of Authors, therefore, we have omitted the Id field. Then, in our Post function we make the following changes:

[HttpPost]
public async Task<ActionResult> Post([FromBody] AuthorCreationDTO authorCreation)
{
  var author = _mapper.Map<Author>(authorCreation);
  _context.Authors.Add(author);
  await _context.SaveChangesAsync();
  var authorDTO = _mapper.Map<AuthorDTO>(author);
  return new CreatedAtRouteResult("GetAuthor", new { id = author.Id }, authorDTO);
}

And, in our AutoMapper configuration, we must place the following line of code:

x.CreateMap<AuthorCreationDTO, Author>();

With this, we are ready to test our creation functionality. In Postman we can make a Post to the corresponding URL, in my case, this is: https://localhost:5001/api/authors/

In the body tab, we select raw and then JSON (application/json) for the content-type. Then, in the body we can place something like the following:

{
"Name": "Daniel Kahneman",
"Birthdate": "1934-03-05"
}

In this way, when executing the previous HTTP Post, we obtain a 201 Created as well, in the headers, we find the Location with the URL corresponding to the newly created resource, and finally, in the body of the response, we are Send the newly created resource.

Conclusion

  • We can use the HttpPost attribute so that an action of our controller responds to an HTTP Post.
  • This is useful when we want the client to send us structured information.
  • When we create a resource, the correct thing is to return a status code 201 Created.
  • The CreatedAtRouteResult class helps us to return a creation response following the good practices of Web APIs.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s