I am insanely thankful to be included in C# Advent this year. This is the 3rd year of C# Advent and I always enjoy the dozens of posts by everyone in the community. Be sure to follow the link above and check out the other posts and watch
#csadvent
on Twitter for updates.
RESTful APIs are very popular these days. When used consistently, they provide a great way to make our APIs easier for users to consume. But how can we make discovering endpoints and capabilities easier? One way is to implement Hypermedia as the Engine of Application State (HATEOAS). You may have seen HATEOAS used in other APIs without realizing it.
Let’s look at two responses from RESTful APIs:
In the example responses, you can see that by adding the links
property to your object, you can greatly increase the discover-ability of your RESTful APIs.
Let’s add a rudimentary implementation of HATEOAS in an ASP.NET Core web API.
permalinkGetting Started
First, download the sample code in this GitHub repository.
The solution contains two C# projects: BaldBeardedBuilder.HATEOAS.Lib (Lib) and BaldBeardedBuilder.HATEOAS (API).
permalinkHATEOAS.Lib (Lib)
We’re not going to go into much detail about the Lib project, but I want to provide a little context to how it’s used. Its sole purpose is to provide an example data access layer. The Lib project contains a small Entity Framework Core DbContext with two related DbSets; Clients and Addresses. The solution will use an in-memory database and will seed it each time you debug.
permalinkHATEOAS (API)
The API project is where all of our HATEOAS magic happens, but before we get into those details, let’s take care of some housekeeping.
As we mentioned previously, we’re using an in-memory database for Entity Framework Core (EF). We’re also using AutoMapper to map our EF entities to the API models. To get those things setup we’ll first add an AutoMapping.cs
file to the root of the API project with the following code:
This code will create the AutoMapper maps between our EF entities and models. With that file in place, we’ll update our Startup.cs
’s ConfigureServices
method with the following:
permalinkPreparing Our Models
Before we can add links to our models, we need to define what they look like. Many times objects will have links that
allow for navigation to related objects. You may even see links related to functionality like CRUD and other operations.
In our example, we’ll add links for relational objects, including _self
. The _self
link will define the path to that
specific object. In the Models
directory of the API project, let’s add a Link.cs
file with the following class:
Next we’ll add a base class that our future models will inherit from. Add a new RestModelBase.cs
file to the Models
folder of the API project with the following:
With that base class in place, we’ll inherit it in our AddressModel
and ClientModel
classes.
permalinkAdding a Controller Base Class
We’ll handle adding the links to each object in a new RestControllerBase
. Add a RestControllerBase.cs
file to the Controllers
folder of the API project. It should inherit from ControllerBase
.
Now add a constructor that receives an IActionDescriptorCollectionProvider and IMapper as shown below.
Next we’ll add a method that will create URIs for each link. The URLLink
method receives the relation as a string that you will specify. Examples would be “_self”, “addresses”, etc. It also takes in the name of the route in MVC. These will be defined later when we build our API controllers. Finally, the method takes in an object of values that will contain our route values. Add the following to the RestControllerBase.cs
.
Now we can add methods to generate links for each of our models. In the repository you’ll see two methods, RestfulAddress
and RestfulClient
, that do this work. We’ll look through the RestfulClient
method to describe what we’re doing.
In the RestfulClient
method above, we take in a Client object and convert it to a ClientModel with AutoMapper. Then we add links for “all”, “_self” and “addresses” paths.
The “all” relationship will provide a link to our API method that returns all clients. The “_self” relationship defines where this client can be retrieved from the API. The “addresses” relationship defines where to find the addresses associated with this client. Each of the methods we define in those links (‘GetClients’, ‘GetClientAsync’ and ‘GetAddressesByClient’) will be defined later in our ClientController.
permalinkAdding Controllers
Now that our base controller is done, let’s add a ClientController.cs
that inherits from our RestControllerBase
class. First, we’ll define a constructor and inject everything our base class needs as well as our DbContext.
Now we can access our database to retrieve data and return it using the RestfulClient
method of our base class.
Our AddressController
will also inherit our RestControllerBase
and its constructor will match the ClientController
. Then we can add the following methods for our /api/addresses
routes:
One thing to notice on these controller methods: each is provided a Name
attribute. These names
correspond to the routeName
’s we pass into the UrlLink
method. The UrlLink
method will use
that method’s template to generate the URL to that route.
permalinkWrap It Up
Now that everything is in place, we can run the application and navigate to the /api/clients
route
to retrieve a list of clients. Those clients should have a links
property with links you can follow
to retrieve additional data or perform various actions.
Remember that links don’t only have to provide URI’s to related data, but can also provide URI’s to
perform tasks. Some implementations include links for CRUD operations with relations like client-update
,
client-delete
, etc.
Hopefully, this basic implementation of HATEOAS gives you some ideas of how you can implement something similar in your own API’s to improve their discoverability. Have any questions or suggestions to improve it? Leave a comment below!