Building a basic REST AspNet Core Example - Part 6

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

In this post I will look at additional HTTP features. That is HTTP status 202 Accepted and the Retry-After header and see how this can be used with our FileController created in the last post.

HTTP Status 202 Accepted

You can read about 202 Accepted in in RFC 7231 section 6.3.3.

The 202 (Accepted) status code indicates that the request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place.

This status code fits well with adding a new file using POST. We could perform a virus scan or do other custom processing before it becomes available. At the time of writing this post AcceptedAtRoute response is only added to the DEV branch of AspNet at GitHub. So we will do a hack adding it to our solution. Likewise IFormCollection is in a newer version than shown here. So here’s the implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[HttpPost]
public IActionResult Post()
{
var file = Request.Form.Files.FirstOrDefault();
if (file == null)
{
return BadRequest();
}
File newFile;
using (var stream = new MemoryStream())
{
file.CopyTo(stream);
newFile = new File(stream.ToArray(), DateTimeOffset.Now, file.ContentType);
fileRepository.Create(newFile);
}
return new AcceptedAtRouteResult("GetFile", new { id = newFile.Id}, null);
}

You should imagine that fileRepository.Create spins off eg. a virus check.

Retry-After Header

You can read about the Retry-After header in RFC 7231 section 7.1.3

Servers send the “Retry-After” header field to indicate how long the user agent ought to wait before making a follow-up request. When sent with a 503 (Service Unavailable) response, Retry-After indicates how long the service is expected to be unavailable to the client.

Here we are going to use Retry-After in another way. That is with the GET request that we pointed to with the AcceptedAtRouteResult. I can imagine this behaviour is debateable, so don’t go do it without considering the consequences. But in this case when then file isn’t available yet we will return No Content status along with the Retry-After header.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public IActionResult Get(string id)
{
var file = fileRepository.Get(id);
if (file == null)
{
return NotFound();
}
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfNoneMatch == null &&
requestHeaders.IfModifiedSince.HasValue
&& requestHeaders.IfModifiedSince.Value >= file.LastModified)
{
return StatusCode(StatusCodes.Status304NotModified);
}
if (!file.IsAvailable)
{
Response.Headers.Add(HeaderNames.RetryAfter, "60");
return NoContent();
}
var responseHeaders = Response.GetTypedHeaders();
responseHeaders.LastModified = file.LastModified;
return File(file.Content, file.ContentType);
}

I have introduced a property IsAvailable on the File class to determine if a No Content along with a Retry-After header should be returned. Now when we make the request to a newly upload file we get:

1
2
3
4
HTTP/1.1 204 No Content
Date: Fri, 16 Dec 2016 10:08:26 GMT
Retry-After: 60
Server: Kestrel

that says we should retry after 60 seconds.

Wrap-up

That’s it for now. But the REST story will continue in other posts.