我需要重写ASP.NET Core 2.2项目中的UrlHelper实现。
我创建了一个名为MyUrlHelper
的类,像这样覆盖了UrlHelper
类
public class MyUrlHelper : UrlHelper
{
public MyUrlHelper(ActionContext actionContext)
: base(actionContext)
{
}
public override string Content(string contentPath)
{
// do something new...
return base.Content(contentPath);
}
}
然后,我创建了一个名为MyUrlHelperFactory
的类,该类实现了IUrlHelperFactory
接口,
public class MyUrlHelperFactory : IUrlHelperFactory
{
public IUrlHelper GetUrlHelper(ActionContext context)
{
return new MyUrlHelper(context);
}
}
最后,我尝试通过在Startup.ConfigureServices()
行之后的services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
方法中添加以下行来替换DI容器中的实现。
但这会引发以下错误
处理请求时发生未处理的异常。 ArgumentOutOfRangeException:索引超出范围。必须为非负数,并且小于集合的大小。
这是堆栈跟踪
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
System.Collections.Generic.List<T>.get_Item(int index)
Microsoft.AspNetCore.Mvc.Routing.UrlHelper.get_Router()
Microsoft.AspNetCore.Mvc.Routing.UrlHelper.GetVirtualPathData(string routeName, RouteValueDictionary values)
Microsoft.AspNetCore.Mvc.Routing.UrlHelper.Action(UrlActionContext actionContext)
Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Action(IUrlHelper helper, string action, string controller, object values, string protocol, string host, string fragment)
Microsoft.AspNetCore.Mvc.ViewFeatures.DefaultHtmlGenerator.GenerateActionLink(ViewContext viewContext, string linkText, string actionName, string controllerName, string protocol, string hostname, string fragment, object routeValues, object htmlAttributes)
Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper.Process(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.TagHelpers.TagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.RunAsync(TagHelperExecutionContext executionContext)
AspNetCore.Views_Shared__Layout.<ExecuteAsync>b__44_1()
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync()
AspNetCore.Views_Shared__Layout.ExecuteAsync()
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)
Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync<TFilter, TFilterAsync>()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
但是,如果禁用框架中的“端点路由”功能,则不会出现任何错误。但是,我想使用端点路由。如何在不禁用端点路由的情况下正确覆盖UrlHelper的实现?
这是我禁用端点路由的方式
services.AddMvc(options =>
{
options.EnableEndpointRouting = false;
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
答案 0 :(得分:2)
Microsoft对UrlHelperFactory的实现告诉我,端点路由有一个不同的url-helper类。
如果在使用端点路由时仅想覆盖UrlHelper,则需要覆盖EndpointRoutingUrlHelper类而不是UrlHelper。为了安全起见,您可能需要覆盖这两个类。
不幸的是,AspNet团队宣布EndpointRoutingUrlHelper类为内部类,因此您不能直接重写它。这并不意味着我们不能复制源代码:)
无论如何,要覆盖UrlHelper,首先要创建我们自己的端点路由助手版本(即MyEndpointRoutingUrlHelper
)
public class MyEndpointRoutingUrlHelper : UrlHelperBase
{
private readonly LinkGenerator _linkGenerator;
public MyEndpointRoutingUrlHelper(
ActionContext actionContext,
LinkGenerator linkGenerator)
: base(actionContext)
{
_linkGenerator = linkGenerator ?? throw new ArgumentNullException(nameof(linkGenerator));
}
public override string Action(UrlActionContext urlActionContext)
{
if (urlActionContext == null)
{
throw new ArgumentNullException(nameof(urlActionContext));
}
var values = GetValuesDictionary(urlActionContext.Values);
if (urlActionContext.Action != null)
{
values["action"] = urlActionContext.Action;
}
else if (!values.ContainsKey("action") && AmbientValues.TryGetValue("action", out var action))
{
values["action"] = action;
}
if (urlActionContext.Controller != null)
{
values["controller"] = urlActionContext.Controller;
}
else if (!values.ContainsKey("controller") && AmbientValues.TryGetValue("controller", out var controller))
{
values["controller"] = controller;
}
var path = _linkGenerator.GetPathByRouteValues(
ActionContext.HttpContext,
routeName: null,
values,
fragment: new FragmentString(urlActionContext.Fragment == null ? null : "#" + urlActionContext.Fragment));
return GenerateUrl(urlActionContext.Protocol, urlActionContext.Host, path);
}
public override string RouteUrl(UrlRouteContext routeContext)
{
if (routeContext == null)
{
throw new ArgumentNullException(nameof(routeContext));
}
var path = _linkGenerator.GetPathByRouteValues(
ActionContext.HttpContext,
routeContext.RouteName,
routeContext.Values,
fragment: new FragmentString(routeContext.Fragment == null ? null : "#" + routeContext.Fragment));
return GenerateUrl(routeContext.Protocol, routeContext.Host, path);
}
public override string Content(string contentPath)
{
// override this method how you see fit
return base.Content(contentPath);
}
}
现在,让我们使用自己的网址帮助器实现IUrlHelperFactory
public class MyUrlHelperFactory : IUrlHelperFactory
{
public IUrlHelper GetUrlHelper(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var httpContext = context.HttpContext;
if (httpContext == null)
{
throw new ArgumentException(nameof(context.HttpContext));
}
if (httpContext.Items == null)
{
throw new ArgumentException(nameof(context.HttpContext.Items));
}
// Perf: Create only one UrlHelper per context
if (httpContext.Items.TryGetValue(typeof(IUrlHelper), out var value) && value is IUrlHelper)
{
return (IUrlHelper)value;
}
IUrlHelper urlHelper;
var endpointFeature = httpContext.Features.Get<IEndpointFeature>();
if (endpointFeature?.Endpoint != null)
{
var services = httpContext.RequestServices;
var linkGenerator = services.GetRequiredService<LinkGenerator>();
urlHelper = new MyEndpointRoutingUrlHelper(context, linkGenerator);
}
else
{
urlHelper = new MyUrlHelper(context);
}
httpContext.Items[typeof(IUrlHelper)] = urlHelper;
return urlHelper;
}
}
最后,在Startup.ConfigureServices(...)
行之后的services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
方法中
添加此行以覆盖已注册的IUrlHelperFactory
实现。
services.Replace(new ServiceDescriptor(typeof(IUrlHelperFactory), new MyUrlHelperFactory()));
我希望这对您有帮助