In part one of this series, I talked about my need to add paging to a web page, how the HTTP range headers work, and how to use them on the client side in AngularJS. If you jumped in here, you can review that here. In this post, I’ll go over how to use the range headers in an ASP.NET Web API controller.
Getting the values
Getting the values for the range header in the Api controller is fairly simple. Inherited from ApiController is a property called Request, which represents the HttpRequestMessage. In the Request is a Headers collection, and in that collection is the Range. If the range header wasn’t there, I typically assume the user wants all the records so I default the from and to values to 0 and the maximum long value. It looks like this:
long fromCustomer = 0;
long toCustomer = long.MaxValue;
var range = Request.Headers.Range;
if (range != null)
{
fromCustomer = range.Ranges.First().From.Value;
toCustomer = range.Ranges.First().To.Value;
}
Returning the Content-Range
This is a little more complicated than getting the values, but still not that bad. The problem is that we don’t have a nice, convenient Response property on our controller. We have to create the response and return that from the method. We need to replace the return of the Get method from IEnumerable<Customer> to a Reponse object that implements IHttpActionResult. This gives us complete control over the Response.
Creating an implementation of IHttpActionResult is not that hard. You only have to implement ExecuteAsync, where you’ll create the response from the request. You’ll also need a constructor for passing in the needed values. Mine looks like this:
public class CustomerGetListResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly List<Customer> _customers;
private readonly long _from;
private readonly long _to;
private readonly long? _length;
public CustomerGetListResult(HttpRequestMessage request,
List<Customer> customers,
long from, long to, long? length)
{
// Save values for the execute later
_request = request;
_customers = customers;
_from = from;
_to = to;
_length = length;
}
public Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken)
{
HttpStatusCode code;
if (_length.HasValue)
{
// status is 206 if there's more data
// or 200 if it's at the end
code = _length - 1 == _to
? HttpStatusCode.OK
: HttpStatusCode.PartialContent;
}
else
{
// status is 200 if we don't know length
code = HttpStatusCode.OK;
}
// create the response from the original request
var response = _request.CreateResponse(code, _customers);
// add the Content-Range header to the response
response.Content.Headers.ContentRange = _length.HasValue
? new ContentRangeHeaderValue(_from, _to, _length.Value)
: new ContentRangeHeaderValue(_from, _to);
response.Content.Headers.ContentRange.Unit = "customers";
return Task.FromResult(response);
}
}
In ExecuteAsync, we have to take care of three things. First, determine the status code we want to return. Second, create the response with the status code and content. Finally, we add the Content-Range header. Notice the overloaded ContentRangeHeaderValue constructor. If you use the two parameter version, the header value will look like “customers 0-19/*” to indicate that the length is unknown.
Now all that we need to do is construct the result and return it from the controller, which I do in one step:
return new CustomerGetListResult(Request,
customerList,
fromCustomer,
fromCustomer + customerList.Count() - 1,
CustomerTable.Count());
Final Thoughts
So that’s pretty much it. Remember to check out Part 1 if you haven’t already seen it. Feel free to leave comments, or constructive criticism, or questions below.
Also, the sample code is in my github at https://github.com/qanwi1970/customer-paging.