Implementing HATEOAS in your asp.netcore webapi…

“cars traveling” by Fahrul Azmi on Unsplash

What is HATEOAS

If you’ve never heard of it, the HATEOAS acronym stands for “Hypermedia As The Engine of Application State”. It’s a model developed by Leonard Richardson that bring the REST model to a new level. You can find a complete description level by level from lvl0 to HATEOAS on Martin Fowler’s blog.

A simplistic explanation is that when you request the API, the response will returns you the object you are looking for, but also some described links to inform you how you can alter that given resource. You can also complete these link list with information about how to fetch sub- or related resources (e.g users/1/addresses, etc.).

A real life example

REST response body for https://localhost:8080/users/0ec5e125-...

{
"name": "John Doh",
"birthdate": "2000–12–31"
}

HATEOAS response body for https://localhost:8080/users/0ec5e125-...

{
"name": "John Doh",
"birthdate": "2000–12–31",
"links": [
{
"href": "http://localhost:8080/users/0ec5e125-5eee-4102-b48d-011a600fd74a",
"rel": "self",
"method": "GET"
},
{
"href": "http://localhost:8080/users/0ec5e125-5eee-4102-b48d-011a600fd74a",
"rel": "update_user",
"method": "PUT"
},
{
"href": "http://localhost:8080/users/0ec5e125-5eee-4102-b48d-011a600fd74a",
"rel": "partially_update_user",
"method": "PATCH"
},
{
"href": "http://localhost:8080/users/0ec5e125-5eee-4102-b48d-011a600fd74a",
"rel": "delete_user",
"method": "DELETE"
}
]
}

Disclaimer

If you’ve read some articles about HATEOAS, you will notice that it’s more than only a response body structure. But to keep this article small, we will focus only on that part and let the remaining work for other articles.

Implementation in asp.netcore 2

To transform your API in a HATEOAS API, the work is not as huge as what you can think first. Here we’ll see how to implement it with asp.netcore. The same can be done with other technologies, but focusing on that one will allow us to use some of the great helpers and features of the dotnet framework.

You can checkout the project from my github repository if you want to dive into the code and let it run on your machine.

Our existing API

In this post, we will assume we already have a basic controller that handle the REST requests for a GET user by id.

namespace Example.Api.Controllers{
[Route("users")]
public class UsersController : Controller
{
// -- here comes also the constructor with DI that was removed on purpose to focus on the endpoint method --
[HttpGet("{id}")]
public IActionResult GetUser(Guid id)
{
if (id == Guid.Empty)
{
return this.BadRequest();
}
if (!this.usersServices.UserExists(id))
{
return this.NotFound($"User with id {id} was not found");
}
var user = this.usersServices.GetUserById(id);
var userDto = Mapper.Map<UserDto>(user);
  return Ok(userDto);
}
}

See the complete file example on the github project.

Transforming the REST API to HATEOAS

Name the controller methods

To allow us to return a dynamically constructed url for each link, we will have to name our methods from the routing attribute. You can see here the change for the GET method, but this has to be done on all your endpoints method.

[HttpGet("{id}")]
// has to be changed to 
[HttpGet("{id}", Name = nameof(GetUser))]

This naming is useful as asp.net core provide us some url helpers through the IUrlHelper interface. The this.urlHelper.Link(nameof(this.GetUser), idObj) method will then construct the url for you. But we will see this later in the article.

Create a DTO for the Link object

As our JSON will contain a link object as part of the user properties, we need to define this LinkDTO structure :

public class LinkDto {
  public string Href { get; private set; }
  public string Rel { get; private set; }
  public string Method { get; private set; }
  public LinkDto(string href, string rel, string method) {
    this.Href = href;
    this.Rel = rel;
    this.Method = method;
  }
}

Create a Link factory method

Now that we have the user and the link objects, we need to merge them.

At this step, either you create a link factory method that returns the IEnumerable<LinkDto> to have a better separation of concerns, or you directly add the links in the user object.

For the sack of simplicity of this article, we will just take a look at the second solution.

As you can see, we create for each rest verb (GET, PUT,…) a link. The url to the endpoint will then be generated by asp.netcore from the IUrlHelper using the route you’ve defined in the controllers class and methods attributes, and the user id that you have from the resource.

Use the Creation method

Last and easiest step, use what we’ve learned above. Just insert the call to this last method before returning the IActionResult.

// change this
return Ok(userDto);
// to this
return Ok(this.CreateLinksForUser(userDto));

See the final result of the UsersController on the github project.

Test your HATEOAS api

Now use your favorite tool (like Postman) to test your HATEOAS endpoint manually, but don’t forget to also add automated tests just in case ;-)