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

  • .NET 11 Preview 5 Focuses on Performance, Productivity and Safer Code

    .NET 11 Preview 5 focuses on under-the-hood runtime performance gains, streamlined APIs and language features that reduce boilerplate, plus built‑in security checks and incremental ASP.NET Core and EF Core improvements aimed at everyday developer productivity.

  • VS Code 1.124 Focuses on Agent Autonomy and Parallel Sessions

    Microsoft's June 2026 VS Code update turns on Autopilot by default and adds background sending for agent sessions.

  • Developing Agentic Systems in .NET: From Concept to Code

    ZioNet founder Alon Fliess previews his Visual Studio Live! San Diego session on building true agentic systems in .NET -- covering the cognitive loop, MCP tool integration, multi-agent orchestration and enterprise hosting and governance with the Microsoft Agent Framework.

  • Mastering AI Development and Building AI Apps with GitHub Copilot

    Two Microsoft experts explain how GitHub Copilot is evolving from a coding assistant into a broader platform for building, customizing and testing AI-powered developer workflows.

Subscribe on YouTube