March 10, 2018

Injecting UrlHelper in ASP.NET Core MVC

One of our APIs has a dynamic routing system that invokes a different handler based on attributes of the incoming HTTP request.

Each of these handlers is responsible for building the API response which includes generating hypermedia links that describe the state and capabilities of the resource, for example:

{
  "total_count": 3,
  "limit": 10,
  "from": "2018-01-25T06:36:08Z",
  "to": "2018-03-10T07:13:24Z",
  "data": [
    {
      "event_id": "evt_b7ykb47ryaouznsbmbn7ul4uai",
      "event_type": "payment.declined",
      "created_on": "2018-03-10T07:13:24Z",
      "_links": {
        "self": {
          "href": "https://example.com/events/evt_b7ykb47ryaouznsbmbn7ul4uai"
        },
        "webhooks-retry": {
          "href": "https://example.com/events/evt_b7ykb47ryaouznsbmbn7ul4uai/webhooks/retry"
        }
      }
    },
  ...
}

To avoid hardcoding paths into these handlers we wanted to take advantage of UrlHelper to build the links. Unlike many components in ASP.NET Core, this is not something that is injectable by default.

To register it with the built-in container, add the following to your Startup class:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddScoped<IUrlHelper>(x => {
    var actionContext = x.GetRequiredService<IActionContextAccessor>().ActionContext;
    var factory = x.GetRequiredService<IUrlHelperFactory>();
    return factory.GetUrlHelper(actionContext);
});

Both IActionContextAccessor and IUrlHelperFactory live in the Microsoft.AspNetCore.Mvc.Core package. If you’re using the Microsoft.AspNetCore.All metapackage you should have this referenced already.

Once done, you’ll be able to use IUrlHelper in any of your components, assuming you’re in the context of a HTTP request:

if (authResponse.ThreeDsSessionId.HasValue)
{
    return new PaymentAcceptedResponse
    {
        Id = id,
        Reference = paymentRequest.Reference,
        Status = authResponse.Status
    }
    .WithLink("self", _urlHelper.PaymentLink(id))
    .WithLink("redirect",
        _urlHelper.Link("AcsRedirect", new { id = authResponse.ThreeDsSessionId }));
}

© 2020 Ben Foster