web-dev-qa-db-fra.com

ASP.Net Core 2.0: création d'UrlHelper sans demande

Je travaille sur la création d'un UrlHelper pour un travailleur en arrière-plan afin de créer des URL de rappel, ce qui signifie que cela ne fait pas partie d'une demande normale où je pourrais simplement le demander via DI.

Dans ASP.Net 5, je pouvais simplement créer une requête HttpRequest et lui donner la même configuration HttpConfiguration que j'ai utilisée pour créer mon application, mais dans ASP.Net Core 2.0, UrlHelper dépend d'un ActionContext complet qui est un peu plus difficile à concevoir.

J'ai un prototype qui fonctionne, mais il utilise un hack méchant pour faire passer les données d'itinéraire hors du processus de démarrage de l'application. Y a-t-il une meilleure manière de faire cela?

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
}
25
Stylpe

Avec ASP.NET Core 2.2 sorti aujourd'hui , ils ont ajouté une classe LinkGenerator qui semble résoudra ce problème (les tests semblent prometteurs ). Je suis impatient de l'essayer, mais comme je ne travaille pas activement sur le projet où j'en avais besoin pour le moment, il faudra attendre un peu. Mais je suis assez optimiste pour marquer cela comme une nouvelle réponse.

3
Stylpe

En parcourant les sources, il semble qu'il n'y ait pas de solution moins hacky.

Dans la méthode UseMvc (), l'objet IRouter en cours de construction est transmis au RouterMiddleware , qui le stocke dans un champ privé et l'expose uniquement aux requêtes. La réflexion serait donc votre seule autre option, qui est évidemment hors course.

Cependant, si vous devez générer uniquement des chemins statiques à l'aide de IUrlHelper.Content (), vous n'aurez pas besoin du routeur comme l'implémentation par défaut ne l'utilisera pas . Dans ce cas, vous pouvez créer l'aide comme ceci:

var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var urlHelper = new UrlHelper(actionContext);
5
Adam Simon