问题
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