How to add the slug to all Link generation in an asp.net core website?

|▌冷眼眸甩不掉的悲伤 提交于 2020-12-02 07:59:46

问题


I need to be able to control the links being generated by my Url.Content("~") call to be able to accept a Slug in the beginning of the link. Basically the hosting URL will be behind a Load-balancer and may be at the root level or behind a friendlier Url...

As an example: The site is configured to run under http://localhost:5001, so Url.Content("~/scripts/site.js") will generate "/scripts/site.js"

this is fine if the browser is coming directly to that url or even to an alias such as www.mysite.com.

But i want o be able to have the flexibility to host the site under www.mysite.com/Slug (think certs and such)...

now my link that was generated goes to www.mysite.com/scripts.site.js which resolves to a 404.

Ideally, the slug can be configured in a custom IUrlHelper, or even a custom LinkGenerator, but i cannot seem to inject those and overwrite the current ones.

I've tried:

services.AddScoped<IUrlHelper>(x =>
            {
                var actionContext = x.GetService<IActionContextAccessor>().ActionContext;
                return new MyCustomUrlHelper(actionContext);
            });

but was unable to get that injected. When i tried debugging, I noticed that if you call the same command in a controller, you get an instance of Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper instead.

Is there a way to change that without creating a custom helper (because that will be missed in some areas and will make debugging near impossible to find the misused helper)


回答1:


Binding IUrlHelper directly has no effect, as MVC internally resolves the instance using a factory. To get an instance of your own custom URL helper in your controllers and razor views, you need to provide a custom implementation of IUrlHelperFactory in your startup class.

The following code snippets allow you to decorate the original URL helper with your own functionality:

In your Startup class, you need to add the custom implementation for IUrlHelperFactory with singleton scope after AddMvc:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddSingleton<IUrlHelperFactory, CustomUrlHelperFactory>();
}

And the custom implementation could look like this:

public class CustomUrlHelper : IUrlHelper
{
    private IUrlHelper _originalUrlHelper;

    public ActionContext ActionContext { get; private set; }

    public CustomUrlHelper(ActionContext actionContext, IUrlHelper originalUrlHelper)
    {
        this.ActionContext = actionContext;
        this._originalUrlHelper = originalUrlHelper;
    }

    public string Action(UrlActionContext urlActionContext)
    {
        return _originalUrlHelper.Action(urlActionContext);
    }

    public string Content(string contentPath)
    {
        return _originalUrlHelper.Content(contentPath);
    }

    public bool IsLocalUrl(string url)
    {
        return _originalUrlHelper.IsLocalUrl(url);
    }

    public string Link(string routeName, object values)
    {
        return _originalUrlHelper.Link(routeName, values);
    }

    public string RouteUrl(UrlRouteContext routeContext)
    {
        return _originalUrlHelper.RouteUrl(routeContext);
    }
}

public class CustomUrlHelperFactory : IUrlHelperFactory
{
    public IUrlHelper GetUrlHelper(ActionContext context)
    {
        var originalUrlHelperFactory = new UrlHelperFactory();
        var originalUrlHelper = originalUrlHelperFactory.GetUrlHelper(context);
        return new CustomUrlHelper(context, originalUrlHelper);
    }
}



回答2:


The IUrlHelper is not injectable by default.

You will have to modify your startup.cs code a bit as explained in this blog.

You will have to first register IActionContextAccessor.

Then with the help of UrlHelperFactory, you can inject your custom implementation as shown below:

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.

This should help you to resolve your problem.




回答3:


Can you not just brute force some flexibility into your solution with string concatenation like this:

public string SlugUrl(string slug, string url, bool tilde = false)
{
    if (tilde) then
    {
        return Url.Content("~" + slug + url);
    }
    else
    {
        return Url.Content(slug + url);
    }
}

[...]

string slug1 = "www.mysite.com";
string slug2 = "www.mysite.com/Slug";
string trailUrl = "/scripts/site.js";
string result1 = SomeClass.SlugUrl(slug1, trailUrl);
string result2 = SomeClass.SlugUrl(slug2, trailUrl);
string result3 = SomeClass.SlugUrl(slug1, trailUrl, true);
string result4 = SomeClass.SlugUrl(slug2, trailUrl, true);

etc...



来源:https://stackoverflow.com/questions/54729700/how-to-add-the-slug-to-all-link-generation-in-an-asp-net-core-website

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!