在ASP.Net Core 2.2 MVC中使用端点路由时,top如何正确覆盖IUrlHelper?

我需要重写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 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,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 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);

chenglong678 回答:在ASP.Net Core 2.2 MVC中使用端点路由时,top如何正确覆盖IUrlHelper?

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

我希望这对您有帮助

本文链接:https://www.f2er.com/3164258.html

大家都在问