Adding links to resource representations is really easy to do within a Web API controller. Using the controller's Url property (which is of UrlHelper class) you can build the href of a link using route names, like this:
var href = Url.Link("RouteName", new { id = 123 });
The value of href is computed based on route configuration - in this case something like http://example.org/api/products/123. This is great because I can adjust route configurations without having to update all the places hrefs are generated. All links emitted conform to the new configurations.
But can this, or any similar route-based technique, be used to create templated hrefs? For example, to create a link from some resource to an arbitrary product using HAL's templated link property:
"_links": {
"self": {
"href": "http://example.org/api/productfinder"
},
"product": {
"href": "http://example.org/api/products/{productId}",
"templated": true
}
}
(I don't really have a resource called productfinder - just a contrived example for this question).
I tried setting the routeValues parameter to a string of the template, e.g.
var href = Url.Link("RouteName", new { id = "{productId}" });
This produces a URL-encoded string: http://example.org/api/products/%7BproductId%7D. I could use .Replace, but this approach fails for another reason. If the route configuration has type constraints e.g. {id:int} then the href becomes null.
I tried passing null to routeValues hoping I could then just append the template portion. Link() returned null, so href ended up being just the template portion. Plus this wouldn't work for complex routes like api/{this}/something/{that}{?q}
So I'm stuck. Can I use routes to generate templated hrefs? If so, how?
Okay, it took me a while, but I've got something that works. The trick is to lookup the
RouteTemplatefrom the configuration's route table. Each controller has a reference to the configuration. From there it was pretty easy.I created an extension class to
ApiController. In practice I will probably subclassApiControllerbut the idea is the same. Also, I may return theUriobject instead of using.ToString().Now, when inside a controller's method and I need a templated href I can do this:
This works well with route attributes. It also works for pre-configured routes, e.g.:
So I can supply the controller name and get the templated href like this:
Again in practice I'll modify and tweak, e.g. add options for absolute/relative, etc. But the core problem of getting templates using route names is now solved.