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

  • Microsoft Agent Framework Makeover: Claws, Loops and Harnesses

    Microsoft's newly released Agent Framework Harness packages the loops, planning, memory, context management and safety controls that developers previously had to assemble around AI models themselves.

  • Visual Studio 2026 Gives Copilot Built-In Skills -- and Makes Them Prove Their Worth

    Microsoft is moving Agent Skills beyond bring-your-own instructions by shipping expert-authored workflows with the IDE, while keeping them off by default until testing shows their benefits justify the additional token use.

  • Copilot AI Billing Shock Met with Meters, Caps and Token-Saving Tools

    GitHub is layering spending limits, expanded credit allowances and increasingly granular usage reporting onto Copilot, while Microsoft is reworking Visual Studio and VS Code to expose -- and reduce -- the cost of agentic development.

  • The AI-Powered Software Development Lifecycle

    René van Osnabrugge makes the case that AI's biggest opportunity in software development is not faster coding -- it's reducing the friction everywhere else in the SDLC.

Subscribe on YouTube