Code Focused

Embellish Your Web API Responses with Header Data

Learn how providing information in available HTTP header elements can help make interactions between unrelated systems straightforward and meaningful.

Web API projects provide a useful and structured way for devices and external systems to interact with your precious server resources. The typical implementation uses a combination of crafted URLs and HTTP message body content to transport data that’s meaningful to both the client and server sides of the conversation. But you can also transmit additional content in the official HTTP header elements. In fact, some commands expect that these elements be used.

As an example, consider the creation of new server-side resources through an HTTP POST operation. The official documentation for the HTTP 1.1 protocol (RFC 2616, section 9.5) says of the POST command, "If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header." That Location header is not only an expected part of the response when a resource is created, it's fairly easy to add.

Create a new C# Web project using the ASP.NET Web Application (.NET Framework) template. When prompted for the project type, choose the Empty type, and select the Web API checkbox in the Add Folders and Core References field. This tells Visual Studio to craft a new project with just the basic tooling required for Web API requests.

In the Solution Explorer panel, right-click on the Controller folder, then choose Add from the shortcut menu. When prompted for the type of scaffolding to add, select Web API 2 Controller - Empty from the list. Give the new controller the name "EntryController." The basic controller class is ready for the API-specific commands:

// ----- Make sure you have all of these imports:
using System;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;

namespace LocationWebApp.Controllers
{
  public class EntryController : ApiController
  {
  }
}

Add a GET command that returns information on already-defined resources. Normally, these would come out of a database or some other persistent storage. Because the focus here isn't on the actual content, the code presented returns pretend data for any resource that has a resource ID from 1 to 10,000:

// ----- GET: ~/api/Entry/id
[Route("api/Entry/{id:int}")]
[HttpGet]
public HttpResponseMessage GetEntry(int id)
{
  // ----- Return the pretend details for this entry.
  if ((id < 1) | (id > 10000))
    return Request.CreateResponse(HttpStatusCode.NotFound);
  else
    return Request.CreateResponse(HttpStatusCode.OK,
      $"Detail for Entry #{id}: Pretend Detail");
}

Time to add the POST command. Maintaining the pretend-data theme, this API command pretends to create a resource, tying it to a randomly generated resource ID:

// ----- POST: ~/api/Entry
[Route("api/Entry")]
[HttpPost]
public HttpResponseMessage PostEntry([FromBody] string bodyDetail)
{
  // ----- Pretend to add an entry to internal storage.
  HttpResponseMessage response;
  string targetPath;
  int newID;

  // ----- Get the new entry ID.
  newID = (new Random()).Next(1, 10000);

  // ----- Message response code added later...
}

Normally, a basic response can be generated with a quick Response.CreateResponse method call, with or without response content. The GET command code already added to this controller included such features. To add a Location header, that response must be modified before it's returned back to the client:

// ----- Build the response with a location.
response = Request.CreateResponse(HttpStatusCode.Created);
targetPath = $"~/api/Entry/{newID}";
response.Headers.Location = RelativeToAbsolutePath(Request, targetPath);
return response;

The Web API response object already includes support for a Location header. Assigning a URI instance to it tells the application to insert the appropriate header in the response. Here's the code that converts a relative (tilde-prefixed) path to a full server-based absolute path and wraps it up in a URI object:

private static Uri RelativeToAbsolutePath(
  HttpRequestMessage requestSource, string relativePath)
{
  Uri baseUri = new Uri(requestSource.RequestUri.AbsoluteUri.Replace(
    requestSource.RequestUri.PathAndQuery, String.Empty));
  return new Uri(baseUri, VirtualPathUtility.ToAbsolute(relativePath));
}

The Web API project is ready to accept resource-creation requests. If you have the curl command-line tool installed on your system (available from the curl.haxx.se Web site), you can issue a test POST command:

REM ----- Adjust this command to include the correct
REM       port number for your test IIS Express instance.
REM       Enter the following on a single command line.
curl -i -X POST -H "Content-Type: application/json"
  -d "{data=12345}" http://localhost:62301/api/Entry

The response includes a Location header, referring the caller to the GET command needed to obtain the resource from the server:

HTTP/1.1 201 Created
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Location: http://localhost:62301/api/Entry/6535
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: <omitted for brevity>
X-Powered-By: ASP.NET
Date: Sun, 02 Jul 2017 15:11:55 GMT
Content-Length: 0

Pasting that location into your browser returns the data generated by the resource GET command, shown here in the default XML response format:

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
  Detail for Entry #6535: Pretend Detail</string>

You aren't required to use the Location header or any other custom headers in your HTTP responses. But as with any communication standard, the HTTP dictates help to make interactions between unrelated systems straightforward and meaningful.

About the Author

Tim Patrick has spent more than thirty years as a software architect and developer. His two most recent books on .NET development -- Start-to-Finish Visual C# 2015, and Start-to-Finish Visual Basic 2015 -- are available from http://owanipress.com. He blogs regularly at http://wellreadman.com.

comments powered by Disqus

Featured

Subscribe on YouTube