If you need to catch up on the previous posts then see part 1 & 2, 3, 4, 5, 6, 7. The source code for this post is at github.
First a disclaimer - this blog is a journey for me, I am no expert in hypermedia - rather a newbie trying it and documenting it here (hence the site name Code Playground). So whether or not I am all into hypermedia is to be determined.
Hypermedia
The third level of Richardson’s maturity model is about hypermedia. Oversimplified it is about also providing links in the response to actions you can take from the given state. HTML has hypermedia controls for this like the form, a, img tags. No-one really needs a manuel or documentation to use a web-site, and hypermedia can be seen as a way to make a protocol more self-documenting, make it less brittle to version changes etc.(if you provide all URI’s to the api then you can be locked in when attempting to change).
Notice Roy Fielding the man who coined REST is pretty strict on hypermedia saying you cannot call it REST without it. Read this post for more on that statement. So no doubt the REST term is misused by many API’s in the wild.
With Json it is more unclear how hypermedia controls should be provided. There is a range of options like
- Json-LD (or look here)
- HAL (notice the status is draft and V8 has just expired)
- JSON API
- Collection+Json
- Siren
The “RESTFul Web APIs: Services for a Changing World” book looks at many of these and may be a read worth. You can also read this post by Kevin Sookocheff that covers these formats. There is more material to this subject than I can cover - and I do not claim to be that familiar with each of the standards. In this post I will look at the first two.
Json-LD - A JSON-based Serialization for Linked Data
This is a W3C recommendation. It builds on the concept of Linked Data that says 1) Use URIs as names for things, 2) use HTTP URIs so that people can lookup the names 3) when someone looks up a URI provide useful information & 4) include links to other URIs, so that people can discover more things.
You may read the Json-LD introduction to understand it further, but basically it provides eg. the ability to use links in your json and to describe the json document and its terms itself. It can be used on an existing API if you wish. There is a nice video here explaining the basics, and another one that continues on the first. Expansion and Compaction is explained here and is useful to watch to get the data exchange idea behind Json-LD.
Let’s look at how Json-LD can be used with our invoice representation. But first let’s look at the response for GET /invoices/1,
|
|
Now when using the property name invoiceDate what exactly do we mean. Json-LD aims high and goes for a common understanding between different web sites of what we mean, so it tries to solve the ambiguity of what that term means. To be specific the property could be specified as an URI (or URL if it can be dereferenced)
|
|
in this case it states we mean an invoice date as defined by restexample. And at the end of this url we provide documentation for this property. We could also have looked in schema.org and picked the URL from there - thus aiming to provide a more crosssite understanding the data, but even within our own company this may be useful. But to fit that understanding into your existing json structure we add a @context to the json. The context tells us how to understand the json data. It allows us to map between the property name (called terms) and the unique name (IRI), as shown here.
|
|
The context does not have to go within your data as shown here. It can be pointed to using the Http Link header. You can also reference it like this
It also introduces the concept of global identifiers to identify the objects, that is to add links.
|
|
So eg. in the case of getting a list of invoices this can be useful with a link for each invoice. You can also specify the type of a property. Let’s say our invoice has a link to sales support
|
|
with the above context we can specify the property salesSupport is to be treated like a link. @type can more generally be used to inform about its type like number, datetime etc. Like shown here.
|
|
There’s much more to Json-Ld than shown here, but these are the basics. You can’t specify the actions you can take given the response. For this you can combine Json-Ld with Hydra. There’s a video on hydra here. Hydra calls these actions “operations”. So you could specify that a delete operation exists like this
|
|
That’s just a simple example - I won’t cover hydra here in detail.
AspNet Core
To be reasonable to add Json-LD to the API some tooling is required. If not then these definitions will just end up out of sync with the implementation. For Json-LD client side operation there is json-ld.net supporting the Json-LD 1.0 Processing Algoritms and API. There is also this one that is not .Net core. So based on a quick google tour there is not something that easily fits the purpose. Let me know if you have the solution.
HAL - JSON Hypertext Application Language
Taken from HAL’s specification:
HAL is a generic media type with which Web APIs can be developed and exposed as series of links. Clients of these APIs can select links by their link relation type and traverse them in order to progress through the application.
Basically it adds two reserved properties _links and _embedded JSON. It’s media type is “application/hal+json”. _links is defined as
It is an object whose property names are link relation types (as defined by [RFC5988]) and values are either a Link Object or an array of Link Objects. The subject resource of these links is the Resource Object of which the containing “_links” object is a property.
To get the understanding of link relation types you can read RFC 5988 section 4 that says,
In the simplest case, a link relation type identifies the semantics of a link. For example, a link with the relation type “copyright” indicates that the resource identified by the target IRI is a statement of the copyright terms applying to the current context IRI. Link relation types can also be used to indicate that the target resource has particular attributes, or exhibits particular behaviours; for example, a “service” link implies that the identified resource is part of a defined protocol (in this case, a service description).
So let’s see the link relation type as a a property name in HAL:
“self” is the link relation property. “self” signifies that the URL in the value of href attribute identifies a resource equivalent to the containing element. IANA has a link relation registry here. So take a look at these before inventing your own.
_embedded is defined as
It is an object whose property names are link relation types (as defined by [RFC5988]) and values are either a Resource Object or an array of Resource Objects. Embedded Resources MAY be a full, partial, or inconsistent version of the representation served from the target URI.
The Linked object can have the following properties href, templated, type, deprecation, name, profile, title, hreflang. But let’s see a constructed example for GET /invoices/
|
|
Here we have a “next” link relation that takes us to be next subset of invoices. _embedded contains a partial representation of the invoice representation with a link to each invoice.
In the above example we are using link relation types registered with IANA. When you need to create your own then RFC 5988 defines “Extension Relations Types” says
Applications that don’t wish to register a relation type can use an extension relation type, which is a URI [RFC3986] that uniquely identifies the relation type. Although the URI can point to a resource that contains a definition of the semantics of the relation type, clients SHOULD NOT automatically access that resource to avoid overburdening its server.
So http://restexample.org/relationTypes/foo could be our own relation type. HAL tells us to provide links - that can be dereferenced in a web browser - that provides documentation. And we can use the CURIE to shorten these links. Here’s an example
|
|
so acme:widgets unfolds to http://docs.acme.com/relations/widgets.
AspNet Core
We can find a list of libraries for HAL here. Most seems out of date (at the time of writing this post) so the choice we have is Halcyon. Basically it provides us with a way to return HAL without changing our existing model using an extension method on the controller.
|
|
So let’s try Halcyon out on the InvoiceController. First change is the GET /invoices/{id}
|
|
the HAL extension is supplied with our model, and the self link (it will add it by itself if not specified, but with a non-standard method property).
The response for /invoices/1 now looks like this:
|
|
so a _links section is added with a self reference. Next change is the GET /invoices/. Here we use the embedded extension method.
|
|
Now we don’t really have any specific data for the collection itself - which is the reason for using null for the model as the first parameter for the HAL method. Then comes the collection link followed by the name of the embedded collection. Next is the embedded collection with its link. The link is specified as a template, that takes the Id from the invoice. Here is the GET
|
|
Providing paging would now be a question of providing a “prev”, “next” link.
Wrap-up
Writing this post I realized that for a newbie to use hypermedia you have a rather large evaluation task to go though.