Building a basic REST AspNet Core Example - Part 4

If you need to catch up on the previous posts then see part 1 & 2, 3. The source code for this post is at github.

Here we are going to take a look at how to tackle the GET /invoices call. I’ll repeat the disclamer - It is going to be pretty naive and basic and I will build on it in further posts (hopefully). We are still at level 2 in Richardson’s maturity model and will not take hypermedia in just yet. We will not even reach a full GET implementation in this post.

GET invoices/ with HTTP Prefer Header

One of the first things that we will have to think about is what resource representation we want. Now we could say we want to return the complete invoices, or we could say we do not want to return lines. Let’s start with the simplest case. We return a list with the full representation.

1
2
3
4
5
6
7
8
[HttpGet]
public IActionResult Get()
{
var invoices = invoiceRepository.GetAll().Select(
invoice => getInvoiceMapper.ToModel(invoice));
return Ok(invoices);
}

but in many cases that may not be what our client is interested in. We could probably cut the invoice lines. There are many ways in “the wild”, but here we are going to look at the HTTP Prefer Header in RFC 7240 as a way to tell the server how we prefer the response. To shortly introduce Prefer, the RFC says:

The Prefer request header field is used to indicate that particular server behaviors are preferred by the client but are not required for successful completion of the request. Prefer is similar in nature to the Expect header field defined by Section 6.1.2 of [RFC7231] with the exception that servers are allowed to ignore stated preferences.

Section 4.2 specifies

The “return=representation” preference indicates that the client prefers that the server include an entity representing the current state of the resource in the response to a successful request.

The “return=minimal” preference, on the other hand, indicates that the client wishes the server to return only a minimal response to a successful request

which is just what we need here. So we want to be able to handle the following requests:

1
2
3
4
5
GET /invoices HTTP/1.1
Prefer: return=representation
GET /invoices HTTP/1.1
Prefer: return=minimal

and the response should state which prefer’s we have handled

1
2
HTTP/1.1 200 OK
Preference-Applied: return=representation

You can also take a look at the parameters that IANA keeps a list of here. But actually we are also required to return the Vary Header so that we do not mess up the HTTP infrastructure like shown here

1
2
3
4
5
HTTP/1.1 200 OK
Date: Wed, 14 Dec 2016 12:08:04 GMT
Content-Type: application/vnd.restexample.finance+json; charset=utf-8
Vary: Prefer,Accept,Accept-Encoding
Preference-Applied: return=representation

Here’s the implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[HttpGet]
public IActionResult Get()
{
var preferHeader = Request.Headers.Prefer();
if (SupportedRepresentations.Contains(preferHeader.Return))
{
Response.Headers.Add("Preference-Applied", "return=" + preferHeader.Return);
Response.Headers.Add("Vary", VaryHeaderValue);
}
var invoices = invoiceRepository.GetAll();
if (preferHeader.Return == ReturnMinimal)
{
return Ok(invoices.Select(invoice => getMinimalInvoiceMapper.ToModel(invoice)));
}
return Ok(invoices.Select(invoice => getInvoiceMapper.ToModel(invoice)));
}

We use a helper method to get the prefer header. If the representation asked for can be fullfilled then we return the required headers. If representation=minimal is specified then we return a list of invoices that has a minimal model as specified here

1
2
3
4
5
6
7
8
public class GetMinimalInvoice
{
public DateTimeOffset InvoiceDate { get; set; }
public DateTimeOffset DueDate { get; set; }
public GetInvoiceCustomer Customer { get; set; }
public decimal SubTotal { get; set; }
public string Id { get; set; }
}

Wrap-up

That concludes the first view on the GET invoices/.