Implementing an API in Web API 2

Wildermuth

Shawn Wildermuth

Welcome to this review of the final module from the Pluralsight course Implementing an API in ASP.NET Web API by Shawn Wildermuth.

Shawn is a 14-time Microsoft MVP (ASP.NET/IIS) and is involved with Microsoft as an ASP.NET Insider, ClientDev Insider and Windows Phone Insider.

He is the author of eight books on software development, and has given talks at a variety of international conferences including TechEd, Oredev, SDC, VSLive, DevIntersection, MIX, DevTeach, DevConnections and DevReach.

The full course also contains modules on:

– Implementing an API in ASP.NET Web API
– API Basics
Securing APIs
– Versioning
– REST Constraints

Web API Version 2

What is Web API 2

Web API 2 is a maturation of the Web API Stack which coincided with the ASP.NET MVC5 release

Unlike the original version of Web API, it requires .NET 4.5 to run.

Version 2 introduces several new features

Converting a Project to Web API 2

This lesson shows how to convert a Web API version 1 project to version 2 based on the official instructions from Microsoft.

First we open up the Nuget Package Manager.

If we aren’t already using it, we need to upgrade to ASP.NET MVC5

Then we find the Microsoft ASP.NET Web API 2 package with the description “This package contains everything you need to host ASP.NET Web API on IIS.” and click update on that.

We also click update on “Microsoft ASP.NET Web API 2 OData”

Earlier in this course CacheCow has been used and we see CacheCow.Server.EntityTagStore.SqlServer updated as well.

Next we update “Microsoft HTTP Client Libraries”

We uninstall a package: Microsoft ASP.NET MVC Fixed DisplayModes has been deprecated so we don’t need it any more.
But when it asks whether we also want to uninstall Mvc 5.x, WebPages 3.x and Razor 3.x we must click No.

We also install “ASP.NET Web Helpers Library”

That is all of the Nuget updates over with. But we’re not done yet because we have some config updates to make.

Shawn shows a number of updates to make in the web.config file.
This is mainly updating the version numbers to the new versions, but we also add a couple of new dependentAssembly bindings: System.Web.Http.WebHost and System.Web.Razor

In appSettings, we update the webpages:Version value to 3.0.0.0

Finally we update the versions from 2.x to 3.x and from 4.x to 5.x in configSections

Now when we build the project we should see 0 errors and 0 warnings.

That was a lot of changes, but all of the changes were to our configuration, not our code.

Attributed Routing

The first new feature in Web API 2 is called Attribute Routing.

Within our WebApiConfig.cs we can add

config.MapHttpAttributeRoutes();

This says to the program “go search all of the controllers for these attributes that we want to use, and create Routes for them”

Web API v1 did versioning with our own controller selector. Shawn comments out that line of code for now.

In Global.asax.cs we have this line that passes the configuration into our Register method:

WebApiConfig.Register(GlobalConfiguration.Configuration);

This has been reversed in Version 2:

GlobalConfiguration.Configure(WebApiConfig.Register);

This advantage of this change is it allows us to defer when the configuration happens: we only action this work when the GlobalConfiguration is ready, not necessarily on start up.

We create a new StatsController and change the inheritance so that it inherits from BaseApiController instead of ApiController

We create a contructor that takes a repository class which was create earlier on in this course.

Next we write a simple Get method, and add a Route attribute above it.

[Route(“api/stats”)]

This defines our route for us. We no longer need to define the Route in the config file.

Attributed Routing Parameters

We add another Get method to our StatsController. Unlike our first Get method, this accepts an integer id parameter. To define the route for this we do:

[Route(“api/stats/{id}”)]

Improved Attributed Routing

We now have duplication in our API as both methods are specifying “api/stats” within their attributed routes.

We can improve this situation by adding RoutePrefix above our class definition:

[RoutePrefix(“api/stats”)]

Now we can remove “api/stats” from each of our Route attributes.

There are some occasions where we might want to override our RoutePrefix. We can do this by using the tilde character:

[Route(“~api/stat/{id})]

Attributed Routing Constraints

We create a third Get method with a string parameter called name and the following Route:

[Route(“~api/stat/{name})]

Now when we run this we see the exception message “Multiple actions were found that math the request”

We can fix this problem by using constraints to give Web API the information it needs to determine the correct routes:

[Route(“~api/stat/{id:int})]
[Route(“~api/stat/{name:alpha})]

Attributed Routing Names

Earlier in the course we used MapHttpRoute on a FoodsController. Here we are going to convert it to using attributes.

We comment out the original MapHttpRoute config and add RoutePrefix to FoodsController.

Next we add Route attributes to each of our methods.

When we run this up, we see the exception message “A route named ‘Food’ could not be found in the route collection”

In the stack trace we see that the ApiController is attempting to get this name and failing.

Earlier in the course a ModelFactory was created, and this uses a urlHelper with the name of “Food” in order to create our link.

To fix our problem, we can add a named parameter into our attribute:

[Route(“{foodid}”, Name=”Food”)]

When we run up our API again we get our food generated and the results displayed as JSON.

However, we’re missing the href of our next page.

We are missing a specific name of a route for an entire controller anymore, we need to have a separately named route for one of our methods:

[Route(“”, Name=”Foods”)]

Advanced Routing

In the Versioning module of the course, Shawn created a ControllerSelector called CountingKsControllerSelector.

In here we are calling controllers.TryGetValue(controllerName, out descriptor)
and we see a scenario where this results in an ArgumentNullException.

The routing uses System.Web.Http.Routing.RouteCollectionRoute and this contains a number of different Routes.

In this demo we see five routes. This route collection is being treated at the same level as our old Routes that were written for Web API version 1.

Versioning, when we’re using Attribute Routes, can be tricky. We can’t depend on the controller name anymore.

To make versioning work for non-attribute routes, we can just do:

if (string.IsNullOrWhiteSpace(controllerName)) {
return base.SelectController(request);
}

Shawn wraps up this lesson by saying on the face of it attribute routing works how we would expect it to: allowing us to do Routes at the controller level, with Routes closely tied to the controller methods.
However, when we deal with lower end code that deals with Route names, we need to think about how we’re using those Routes in a different way.

CORS Support

The idea behind CORS is to allow other websites to call into our Web APIs.
By default only calls from our own domain, or a non website such as a mobile app, are going to be supported.

It is easy to enable CORS in Web API 2, but we first need to download a separate Nuget Package “Microsoft ASP.NET Web API 2 Cross-Origin Support”

There is also a package called “Microsoft ASP.NET Corss-Origin Support” which has basic support. The Web API 2 specific package includes the basic support and more.

Once installed we can enable it like this:

config.EnableCors();

We now have access to the EnableCors attribute:

To allow all domains with any headers and any HTTP methods access to our API:
[EnableCors(“*”,”*”,”*”)]

To restrict to only foo.com when it has a specific header and only GET methods:

[EnableCors(“http://foo.com”,”X-OURAPP”,”GET”)]

We can set this attribute either at the controller level or the method level. We can also disable specific methods within our controller like so:

[DisableCors()]

We can also enable CORS globally (i.e. on every controller) like this:

var attr = new EnableCorsAttribute(“*”,”*”, “GET”);
config.EnableCors(attr);

What is IHttpActionResult?

This is a standard way of constructing common results that we’re going to be frequently returning.

We can use it as a replacement for HttpResponseMessage.

This is a factory pattern, or more precisely an object that supports IHttpActionResult is a factory for returning an HttpResponseMessage.

public interface IHttpActionResult
{
Task ExecuteAsync(CancellationToken cancellationToken);
}

The object that implements this interface is going to allow it to be called asynchronously.

ExecuteAsync is passed in a CancellationToken, and expects to eventually get an HttpResponseMessage.

Some of the built in IHttpActionResult types are:

OkResult
OkNegotiatedContentResult
NotFoundResult
BadRequestResult

Many of these are new methods on the ApiController class

Using IHttpActionResult

In this demo we change the result type from HttpResponseMessage to IHttpActionResult.

Instead of returning Request.CreateResponse(results) we just call Ok(results)

If we want to return a NotFound response then we just type NotFound();

We see that the code is now a lot simpler and more self describing.

Shawn asks where can we use IHttpActionResult in our own code?

IHttpActionResult gives us control over constructing our response messages.

We create a new class VersionedActionResult which derives from IHttpActionResult, and implement the interface.

We add a constructor that takes an HttpResponseMessage, a string and a generic type, and do our constructor injection.

This demo assigns a response to msg and adds a XXX-OurVersion header to it.
Then it returns the result as a Task.

This example is synchronous code and Shawn says the interface requires it to look and pass back a Task, so we’re allowing that with Task.FromResult

We could however use async and await to do asynchronous programming.

Next we go to our BaseApiController and add a new method called Versioned, which returns an instance of our VersionedActionResult.

We can now have more generic code in our controllers.

In FoodsController we change our Get method to return IHttpActionResult instead of our FoodModel, and wrap our result with our new Versioned method.

Shawn runs up Fiddler and we see how easily we can change the value of our XXX-OurVersion header.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s