2017-09-07 33 views
14

我正在为后台工作人员创建UrlHelper来创建回调URL,这意味着它不是正常请求的一部分,我可以通过DI请求它。ASP.Net核心2.0:无需请求创建UrlHelper

在ASP.Net 5中,我可以创建一个HttpRequest,并为它创建与我的应用程序相同的HttpConfiguration,但在ASP.Net Core 2.0中,UrlHelper依赖于完整的ActionContext,这有点难以制作。

我有一个工作原型,但它使用一个令人讨厌的黑客从应用程序启动过程中走私路由数据。有一个更好的方法吗?

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 => svc.AddSingleton<Capture>()); 
    } 

    // Step 2: Swipe the route data in application startup 
    public static IApplicationBuilder UseMvcAndSniffRoutes(this IApplicationBuilder app) 
    { 
     var capture = app.ApplicationServices.GetRequiredService<Capture>(); 
     IRouteBuilder capturedRoutes = null; 
     app.UseMvc(routeBuilder => 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) 
     => 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<Capture>(); 
     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<Startup>() 
      .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<string> Get() 
    { 
     return new string[] { "value1", "value2" }; 
    } 

    // etc 
} 
+0

这可能是一个可怕的想法,但你不能只保存*的请求,所以你可以继续使用它从后台线程期间创建的任何* UrlHelper?所以,你会在第一个请求发出后启动该线程*。 – poke

+0

你有机会检查这个吗? https://stackoverflow.com/questions/37322076/injection-of-iurlhelper-in-asp-net-core-1-0-rc2 –

+0

@poke,这可能会工作,但它有明显的缺点,所以我会只能用它作为最后的手段。 – Stylpe

回答

1

浏览来源似乎没有少hacky的解决方案。

在UseMvc()方法中,正在构建的IRouter对象是passed to the RouterMiddleware,其中stores it in a private field仅将其公开给请求。所以反思会是你唯一的选择,显然已经不适用了。

但是,如果您只需要使用IUrlHelper.Content()生成静态路径,则不需要路由器作为default implementation won't use it。在这种情况下,你可以创建这样的帮手:

var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); 
var urlHelper = new UrlHelper(actionContext); 
+0

这就是我得到的相同结论,这导致了我的问题的解决方法。关于Content()的好处,但我们想链接到一个命名的控制器动作。感谢您的肯定:)我的下一步可能会尝试在Github上制定一个问题,以使这更容易。 – Stylpe