Strongly typed direct routing link generation in ASP.NET Web API with Drum

ยท 695 words ยท 4 minutes to read

ASP.NET Web API provides an IUrlHelper interface and the corresponding UrlHelper class as a general, built-in mechanism you can use to generate links to your Web API routes. In fact, it’s similar in ASP.NET MVC, so this pattern has been familiar to most devs for a while.

The main problem of it is that it’s based on magic strings, as, to generate a link, the route name has to be passed as a string literal. Moreover, all the parameters that are required to built up the link, are simply a set of name-values, represented by a dictionary or an anonymous object, which is hardly optimal. Code is not coherent, refactoring becomes a pain and in general error potential is high.

My friend, and one of the most respected folks in the Web API community, Pedro Felix, has created a library called Drum, designed to avoid the pitfalls of the UrlHelper, allowing you to build links for Web API direct routing (introudced in Web API 2) in a strongly typed way.

Drum works with any direct routing provider, including my own Strathweb.TypedRouting.

Getting started ๐Ÿ”—

You can add Drum to your Web API application by simply grabbing it off Nuget:

install-package Drum  

Once it’s in, you need to enable Drum, which is done against your instance of HttpConfiguration.

config.MapHttpAttributeRoutesAndUseUriMaker();  

Alternatively, you can pass in your own IDirectRouteProvider, such as Starthweb.TypedRouting:

config.MapHttpAttributeRoutesAndUseUriMaker(new TypedDirectRouteProvider());  

In either case, this is replacing your usual call to MapHttpAttributeRoutes.

The registration shown above, wires up all the necessary Drum infrastructure and enables you to reach into the current HttpRequestMessage at any time, and obtain an instance of UriMaker, which you can then use for constructing URIs in a strongly typed manner. To do that, use the relevant extension method, as shown below:

public class MyController : ApiController  
{  
public HttpResponseMessage GetAll()  
{  
//omitted for brevity  
}

public HttpResponseMessage GetById(int id)  
{  
//omitted for brevity  
}  
}

//generate links to this controller accordingly:  
var uriMaker = request.TryGetUriMakerFor<MyController>();

Uri link = uriMaker.UriFor(c => c.GetAll());  
Uri differentLink = uriMaker.UriFor(c => c.GetById(7));  

For links requiring a body (complex parameter), you can use the helper Param to stub any value, since they would not affect URI anyway:

public class MyController : ApiController  
{  
public HttpResponseMessage Put(int id, MyModel model)  
{  
//omitted for brevity  
}  
}

Uri link = uriMaker.UriFor(c => c.Put(7, Param<MyModel>.Any));  

The main advantage of such approach is obviously you now have compile-time safety of your links.

Under the hood ๐Ÿ”—

Drum stores the instance of a UriMakerContext in the Properties dictionary of the HttpConfiguration (as a singleton). That class is a de facto factory for UriMaker instances, which are directly responsible for building up URIs whenever you need them.

When you call the MapHttpAttributeRoutesAndUseUriMaker extension method against your HttpConfiguration, the library will wrap your IDirectRouteProvider (if you don’t hve a custom one, it will simply wrap the built-in attribute routing) with its own internal IDirectRouteProvider (a classic example of a decorator pattern), and will create a mapping between all routes and all of the MethodInfo to which the routes are pointing.

This mapping is stored in the private field of UriMakerContext and is later used by UriMaker to generate links - as it will use the MethodInfo from your expression (such as i.e. uriMaker.UriFor(c => c.GetAll());) to look up the corresponding route. Then, internally, using the route name, UriMaker uses UrlHelper to construct the link.

To do that, UriMaker obviously needs the route parameters (route values) as well - so it converts the parameters that you pass into the strongly typed expression, into Dictionary<string, object>, which is what is required by UrlHelper.

It’s all pretty clever if you ask me, and works really well.

Next ๐Ÿ”—

I am very excited about Pedro’s work. It’s always these little add-ins and libraries that you can throw into your framework, to solve some of its deficiencies (such as magic string link generation that Web API offers), that make your everyday coding enjoyable.

I already had a small contribution to the library, and I’m sure Pedro will be happy to hear what you think, and maybe what you’d change or suggest.

So again, a reminder that you can find Drum at Github.

About


Hi! I'm Filip W., a cloud architect from Zรผrich ๐Ÿ‡จ๐Ÿ‡ญ. I like Toronto Maple Leafs ๐Ÿ‡จ๐Ÿ‡ฆ, Rancid and quantum computing. Oh, and I love the Lowlands ๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ.

You can find me on Github and on Mastodon.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP