title: ASP.Net Core 2.0: Creating UrlHelper without request
body: <p>I'm working on creating a UrlHelper for a background worker to create callback urls, which means it's not part of a normal request where I could just ask for it through DI.</p>
  
  <p>In ASP.Net 5 I could just create a HttpRequest and give it the same HttpConfiguration I used to build my app, but in ASP.Net Core 2.0 the UrlHelper depends on a full ActionContext which is a bit harder to craft.</p>
  
  <p>I have a working prototype, but it's using a nasty hack to smuggle the route data out of the application startup process. Is there a better way to do this?</p>
  
  <pre><code>public class Capture
  {
      public IRouter Router { get; set; }
  }
  
  public static class Ext
  {
      // Step 1: Inject smuggler when building web host
      public static IWebHostBuilder SniffRouteData(this IWebHostBuilder builder)
      {
          return builder.ConfigureServices(svc =&gt; svc.AddSingleton&lt;Capture&gt;());
      }
  
      // Step 2: Swipe the route data in application startup
      public static IApplicationBuilder UseMvcAndSniffRoutes(this IApplicationBuilder app)
      {
          var capture = app.ApplicationServices.GetRequiredService&lt;Capture&gt;();
          IRouteBuilder capturedRoutes = null;
          app.UseMvc(routeBuilder =&gt; capturedRoutes = routeBuilder);
          capture.Router = capturedRoutes?.Build();
          return app;
      }
  
      // Step 3: Build the UrlHelper using the captured routes and webhost
      public static IUrlHelper GetStaticUrlHelper(this IWebHost host, string baseUri)
          =&gt; GetStaticUrlHelper(host, new Uri(baseUri));
      public static IUrlHelper GetStaticUrlHelper(this IWebHost host, Uri baseUri)
      {
          HttpContext httpContext = new DefaultHttpContext()
          {
              RequestServices = host.Services,
              Request =
                  {
                      Scheme = baseUri.Scheme,
                      Host = HostString.FromUriComponent(baseUri),
                      PathBase = PathString.FromUriComponent(baseUri),
                  },
          };
  
          var captured = host.Services.GetRequiredService&lt;Capture&gt;();
          var actionContext = new ActionContext
          {
              HttpContext = httpContext,
              RouteData = new RouteData { Routers = { captured.Router }},
              ActionDescriptor = new ActionDescriptor(),
          };
          return new UrlHelper(actionContext);
      }
  }
  
  // Based on dotnet new webapi
  
  public class Program
  {
      public static void Main(string[] args)
      {
          BuildWebHost(args);//.Run();
      }
  
      public static IWebHost BuildWebHost(string[] args)
      {
          var captured = new Capture();
          var webhost = WebHost.CreateDefaultBuilder(args)
              .SniffRouteData()
              .UseStartup&lt;Startup&gt;()
              .Build();
  
          var urlHelper = webhost.GetStaticUrlHelper("https://my.internal.service:48923/somepath");
          Console.WriteLine("YO! " + urlHelper.Link(nameof(ValuesController), null));
          return webhost;
      }
  }
  
  public class Startup
  {
      public Startup(IConfiguration configuration)
      {
          Configuration = configuration;
      }
  
      public IConfiguration Configuration { get; }
  
      // This method gets called by the runtime. Use this method to add services to the container.
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddMvc();
      }
  
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      public void Configure(IApplicationBuilder app, IHostingEnvironment env, Capture capture)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
  
          app.UseMvcAndSniffRoutes();
      }
  }
  
  [Route("api/[controller]", Name = nameof(ValuesController))]
  public class ValuesController : Controller
  {
      // GET api/values
      [HttpGet]
      public IEnumerable&lt;string&gt; Get()
      {
          return new string[] { "value1", "value2" };
      }
  
      // etc
  }
  </code></pre>