简体   繁体   中英

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

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)

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);
    }
}

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.

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...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM