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
#csadventon 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.
First, download the sample code at https://github.com/MichaelJolley/aspnetcore-hateoas.
The solution contains two C# projects: BaldBeardedBuilder.HATEOAS.Lib (Lib) and BaldBeardedBuilder.HATEOAS (API).
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.
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
ConfigureServices method with the following:
Preparing 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 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
Adding 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
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
Now we can add methods to generate links for each of our models. In the repository you’ll see two methods,
RestfulClient, that do this work. We’ll look through the
RestfulClient method to describe what we’re doing.
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.
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.
AddressController will also inherit our
RestControllerBase and its constructor will match the
ClientController. Then we can add the following methods for our
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.
Wrap It Up
Now that everything is in place, we can run the application and navigate to the
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
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!