8 msdn magazine Cutting Edge
Note that this is the same code you’d use to add a Web API con-
troller to an ASP.NET MVC application. You also have to specify
routes. Here’s some code you want to run at application startup:
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional });
Unless otherwise annotated with the NonAction attribute, any
public methods on the class that match the default naming and
routing conventions are public HTTP-callable endpoints. Th ey
can be called from any client without the need for generated proxy
classes, web.confi g references or special code.
Routing conventions in Web API dictate the URL starts with /api
followed by the controller name. Note that there’s no action name
clearly expressed. Th e action is determined by the type of request,
whether GET, PUT, POST or DELETE. A method name that begins
with Get, Put, Post or Delete is conventionally mapped to the corre-
sponding action. For example, a method GetTasks on a TaskController
will be invoked for any GET request to a URL such as /api/task.
Regardless of the apparent similarity of behavior and class
names with ASP.NET MVC, Web API lives in a completely
separate set of assemblies and uses a completely diff erent set of
types—System.Net.Http is the primary assembly.
Inside Web API Content Negotiation
“Content negotiation” is often used to describe the process of
inspecting the structure of an incoming HTTP request to fi gure
out the formats in which the client wishes to receive responses.
Technically, though, content negotiation is the process in which
client and server determine the best possible representation format
to use in their interactions. Inspecting the request typically means
looking into a couple of HTTP headers such as Accept and
Content-Type. Content-Type, in particular, is used on the
server for processing POST and PUT requests and on the client
for choosing the formatter for HTTP responses. Content-Type is
not used for GET requests.
The internal machinery of content negotiation, however, is
much more sophisticated. Th e aforementioned scenario is the most
typical—because of default conventions and implementations—
but it isn’t the only one possible.
Th e component that governs the negotiation process in Web
API is the class called DefaultContentNegotiator. It implements a
public interface (IContentNegotiator), so you can replace it entirely
if needed. Internally, the default negotiator applies several distinct
criteria in order to fi gure out the ideal format for the response.
The negotiator works with a list of registered media type
formatters—the components that actually turn objects into a specifi c
format. Th e negotiator goes through the list of formatters and stops at
the fi rst match. A formatter has a couple of ways to let the negotiator
know it can serialize the response for the current request.
Th e fi rst check occurs on the content of the MediaTypeMappings
collection, which is empty by default in all predefi ned media type
formatters. A media type mapping indicates a condition that, if
verified, entitles the formatter to serialize the response for the
ongoing request. Th ere are a few predefi ned media type mappings.
One looks at a particular parameter in the query string. For exam-
ple, you can enable XML serialization by simply requiring that an
xml=true expression is added to the query string used to invoke
Web API. For this to happen, you need to have the following code
in the constructor of your custom XML media type formatter:
MediaTypeMappings.Add(new QueryStringMapping("xml", "true", "text/xml"));
In a similar way, you can have callers express their preferences by
adding an extension to the URL or by adding a custom HTTP header:
MediaTypeMappings.Add(new UriPathExtensionMapping("xml", "text/xml"));
MediaTypeMappings.Add(new RequestHeaderMapping("xml", "true",
StringComparison.InvariantCultureIgnoreCase, false,"text/xml"));
For URL path extension, it means the following URL will map
to the XML formatter:
http://server/api/news.xml
Note that for URL path extensions to work you need to have an
ad hoc route such as:
config.Routes.MapHttpRoute(
name: "Url extension",
routeTemplate: "api/{controller}/{action}.{ext}/{id}",
defaults: new { id = RouteParameter.Optional }
);
For custom HTTP headers, the constructor of the Request-
HeaderMapping class accepts the name of the header, its expected
value and a couple of extra parameters. One optional parameter
indicates the desired string comparison mode, and the other is a
Boolean that indicates if the comparison is on the entire string. If
the negotiator can’t fi nd a match on the formatter using the media
type mapping information, it looks at standard HTTP headers such
as Accept and Content-Type. If no match is found, it again goes
through the list of registered formatters and checks whether the
return type of the request can be serialized by one of the formatters.
To add a custom formatter, insert something like the follow-
ing code in the startup of the application (for example, in the
Application_Start method):
config.Formatters.Add(xmlIndex, new NewsXmlFormatter());
Customizing the Negotiation Process
Most of the time, media type mappings let you easily fulfi ll any
special requirements for serialization. However, you can always
replace the default content negotiator by writing a derived class
and overriding the MatchRequestMediaType method:
protected override MediaTypeFormatterMatch MatchRequestMediaType(
HttpRequestMessage request, MediaTypeFormatter formatter)
{
...
}
You can create a completely custom content negotiator with a new
class that implements the IContentNegotiator interface. Once you
have a handmade negotiator, you register it with the Web API runtime:
GlobalConfiguration.Configuration.Services.Replace(
typeof(IContentNegotiator),
new YourOwnNegotiator());
Th e preceding code usually goes in global.asax or in one of those
handy config handlers that Visual Studio creates for you in the
ASP.NET MVC Web API project template.
Controlling Content Formatting from the Client
Th e most common scenario for content negotiation in Web API
is when the Accept header is used. Th is approach makes content
formatting completely transparent to your Web API code. Th e caller
sets the Accept header appropriately (for example, to text/xml) and
the Web API infrastructure handles it accordingly. Th e following