Skip to content

IHateoas Abstraction usage

Icaro "Stuart" Torres edited this page Feb 22, 2023 · 1 revision

The next examples will demonstrate how to use the IHateoas and IHateoas<T> abstractions to generate HATEOAS links in an ASP.NET Core controller.

Output

The generated links contain information about the available actions that can be performed on the Member object, along with the URLs to perform those actions. The rel attribute in each link represents the relationship between the current resource and the linked resource, while the method attribute represents the HTTP method used to access the linked resource.

Default interface

Let's assume we have a MembersController in our application with an action that retrieves a Member by ID and returns it as JSON. The following code snippet provides an example of how to use IHateoas in an ASP.NET Core Web API controller.

using HateoasNet;
using System.Linq;
using System.Collection.Generics;

[ApiController]
[Route("[controller]")]
public class MembersController : ControllerBase
{
    private readonly IHateoas _hateoas;
    private readonly List<Member> _members;

    public MembersController(List<Member> members, IHateoas hateoas)
    {
        _hateoas = hateoas;
        _members = members;
    }

    [HttpGet("{id:guid}", Name = "get-member")]
    public IActionResult Get(Guid id)
    {
        var member = _members.SingleOrDefault(i => i.Id == id);
        var links = _hateoas.Generate(member);
        return member != null ? Ok(new { data = member, links }) : NotFound() as IActionResult;
    }
}

In the constructor of the MembersController, we inject the IHateoas implementation along with any other of its dependencies. Within the Get method, the controller first retrieves the member from the list of members based on the provided ID. If the member is found, the controller generates HATEOAS links for the member using the Generate method of the IHateoas instance taking an object of any type. In this case, we pass in the Member object retrieved from the list. Finally, the controller returns a 200 OK response along with the member data and the generated links, or a 404 Not Found response if the member is not found.

Note that the HATEOAS links generated by the IHateoas instance are based on the configurations defined in the IHateoasSourceBuilder implementations. In the previous examples, we showed how to define such configurations for different types of resources like Member with the MemberHateoasBuilder.

Generic Interface

The generic version of the IHateoas interface allows you to specify the TOutput type for resources for which you want to generate HATEOAS links. This is useful when you want to generate links expressed different than default List<IHateoasLink>, in this case ImmutableDictionary<string, object>>.

using HateoasNet;
using System.Linq;
using System.Collections.Generic;

[ApiController]
[Route("[controller]")]
public class GuildsController : ControllerBase
{
    private readonly IHateoas<ImmutableDictionary<string, object>> _hateoas;
    private readonly List<Guild> _guilds;

    public GuildsController(List<Guild> guilds, IHateoas<ImmutableDictionary<string, object>> hateoas)
    {
        _hateoas = hateoas;
        _guilds = guilds;
    }

    [HttpGet("{id:guid}", Name = "get-guild")]
    public IActionResult Get(Guid id)
    {
        var guild = _guilds.SingleOrDefault(i => i.Id == id);
        var links = _hateoas.Generate(guild);
        return guild != null ? Ok(new { data = guild, links }) : NotFound() as IActionResult;
    }
}

We're using the generic version of the IHateoas interface to generate links for a Guild resource. We've specified the generic type as ImmutableDictionary<string, object> to shape the links property in the final output object.

The GuildsController constructor takes IHateoas<ImmutableDictionary<string, object>> and any of its dependencies as arguments. The List<Guild> argument is used to retrieve the requested guild by ID. If the guild is found, then generates HATEOAS links for the guild, which are returned along with the guild data in an 200 Ok response. If the guild is not found, a 404 Not Found response is returned.

Implementation

In this code snippet, we see an implementation of the generic interface IHateoas with help of AbstractHateoas<ImmutableDictionary<string, object>> base implementations.

using HateoasNet;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;

public class DictionaryHateoas : AbstractHateoas<ImmutableDictionary<string, object>>
{
    public DictionaryHateoas(IHateoas hateoas) : base(hateoas)
    {
    }

    protected override ImmutableDictionary<string, object> GenerateCustom(IEnumerable<HateoasLink> links)
    {
        return links.ToImmutableDictionary(x => x.Rel, x => (object)new { x.Href, x.Method });
    }
}

The DictionaryHateoas class provides the implementation of the abstract GenerateCustom method which generates a ImmutableDictionary<string, object> of links with their respective href and method. This method is called by the base AbstractHateoas<ImmutableDictionary<string, object>> implementation of IHateoas<ImmutableDictionary<string, object>>. Its constructor takes an IHateoas root interface that knows how to generate links for any configured resource type.

Integration

In addition to the built-in IHateoas interface, you can also create your own implementations of the IHateoas<T> interface to customize the generated HATEOAS links for specific T output types.

To register the custom IHateoas<T> implementations, you can use the RegisterAllCustomHateoas extension method on the IServiceCollection object. Here's an example of how to register all custom IHateoas<T> implementations found in the assembly that contains you Startup class:

using HateoasNet.DependencyInjection.Core;

public void ConfigureServices(IServiceCollection services)
{
    // other registrations...

    // optional step to register all custom IHateoas<T> implementations in separated files found in a given assemblies
    services.RegisterAllCustomHateoas(new Assembly[] { typeof(Startup).Assembly }, ServiceLifetime.Scoped);

    // other registrations...
}

This code registers all custom IHateoas<T> implementations found in the assembly that contains the Startup class as Scoped services. You can replace typeof(Startup).Assembly with the assembly that contains your custom IHateoas<T> implementations.

Clone this wiki locally