Implementing JSON API in ASP.NET Core — Part 1
By: Robert Matusewicz
Tags: Api Development, Csharp, Programming, API

Photo by Sean Lim on Unsplash

Some time ago I was made aware of the existence of JSON API Specification. On the page of the specification you can read that:

JSON:API is a specification for how a client should request that resources be fetched or modified, and how a server should respond to those requests. JSON:API is designed to minimize both the number of requests and the amount of data transmitted between clients and servers. This efficiency is achieved without compromising readability, flexibility, or discoverability.

I skimmed through the specification, looked at few examples online and started to wonder how to use it in one of my side project. I looked at few implementations of the specification, but I did not find any that I could use in my project — for many reasons, but two core ones were: there is no library that is supported on .NET 5, and majority of the libraries are not flexible enough for my use case.

So I decided I will write my own media type formatter, and document the process of writing it in a series of blog posts!

Preparing a simple API

For the purpose of this blog post, we will create a simple Users API. This API will currently support only a GET /users/{id} that will return a single, hard-coded resource.

Create new project:

dotnet new webapi -o Users.Api

and add a User DTO and Users Controller:

Run the sample project:

dotnet watch run

Now, if you execute following GET request to the server:

GET /users/c2425f35-c823–455b-8db6-b42729e6ee12

Accept: application/json

You will receive following response:

{

"id": "c2425f35-c823–455b-8db6-b42729e6ee12",

"name": "John Doe",

"email": "john.doe@anonymy.com"

}

I hope you did not have a problem up to this point, and you tested your app before reading further!

The Goal

The goal of this post is to enable the JSON API on our API. That means that we will need to change the implementation in a way that instead of the response we see above, we will receive:

{

"data": {

"type": "users",

"id": "c2425f35-c823–455b-8db6-b42729e6ee12",

"attributes": {

"name": "John Doe",

"email": "john.doe@anonymy.com"

}

}

Instead of adding all the JSON API elements in one go, we just focus on the simplest implementation first. We will worry about links, relationship, etc. later!

Custom formatter for JSON API media type

There is probably more than one way of enabling for JSON API support in .NET 5. I decided to try a solution that is described in Custom formatters in ASP.NET Core Web API.

To register new media type output formatter we need:

create a new class that inherits from TextOutputFormatter and overrides WriteResponseBodyAsync method

and overrides method add instance of that class to OutputFormatters in Startup.cs

Let’s start with JsonApiOutputFormatter — our OutputFormatter for JSON API:

The constructor is very simple. It does two things: registers the new media type — application/vnd.api+json (which is media type for JSON API), and it configures UTF8 as supported encoding. Nothing special.

The WriteResponseBodyAsync method is more interesting, but before we start digging into it, let’s look at two classes that this method will use: JsonApiDocument and ResourceObject:

They capture a basics structure of JSON API Document and Resource Object.

Resource Object contains three properties:

Type — representing the type of resource object.

Id — a unique identifier for resource object

Attributes — a list of attributes of resource object

We will assume that the Type property will always be set to the type name, in our case it’s “user”. The Id will be a UUID identifying the user, and Attributes will be other properties of the object, in this case name and email.

Now lets look at the method implementation.

It gets all the properties of the User object, filters out the Id property, and use their names and values to create a single ResourceObject that is then assigned to Data property of JsonApiDocument object. When the object is ready, we serialize it and write to the HTTP response.

The last thing that we need to do is to update the Startup.cs, so the ASP.NET Core framework know that we want to use our formatter. Add the following line to the ConfigureServices method implementation:

Output

The code is done, we can now build and run the project:

dotnet watch run

and send GET request to the server:

GET /users/c2425f35-c823–455b-8db6-b42729e6ee12

If your implementation is correct, you should receive a response that looks like this:

{

"data": {

"type": "user",

"id": "4cb47cdd-fe2e-4c85-b33b-fa4e335fe659",

"attributes": {

"name": "John Doe",

"email": "john.doe@anonymy.com"

}

}

}

This is exactly the output that we defined in The Goal section!

The end of part 1

I hope you enjoyed this blog post. If yes, please leave some feedback in the comment.

The code that was used in this blog post is available here.