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

  • Hands On: New VS Code Insiders Build Creates Web Page from Image in Seconds

    New Vision support with GitHub Copilot in the latest Visual Studio Code Insiders build takes a user-supplied mockup image and creates a web page from it in seconds, handling all the HTML and CSS.

  • Naive Bayes Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the naive Bayes regression technique, where the goal is to predict a single numeric value. Compared to other machine learning regression techniques, naive Bayes regression is usually less accurate, but is simple, easy to implement and customize, works on both large and small datasets, is highly interpretable, and doesn't require tuning any hyperparameters.

  • VS Code Copilot Previews New GPT-4o AI Code Completion Model

    The 4o upgrade includes additional training on more than 275,000 high-quality public repositories in over 30 popular programming languages, said Microsoft-owned GitHub, which created the original "AI pair programmer" years ago.

  • Microsoft's Rust Embrace Continues with Azure SDK Beta

    "Rust's strong type system and ownership model help prevent common programming errors such as null pointer dereferencing and buffer overflows, leading to more secure and stable code."

  • Xcode IDE from Microsoft Archrival Apple Gets Copilot AI

    Just after expanding the reach of its Copilot AI coding assistant to the open-source Eclipse IDE, Microsoft showcased how it's going even further, providing details about a preview version for the Xcode IDE from archrival Apple.

Subscribe on YouTube

Upcoming Training Events