aspnetcore: ActionContext.RouteData.Routers empty causes ArgumentOutOfRangeException in UrlHelper (Regression from 2.1)

Describe the bug

  1. ActionContext.RouteData.Routers is empty collection with CompabilityVersion.Version_2_2
  2. UrlHelper uses direct access to zero-element, instead calling .First() or .FirstOrDefault() with emergency return of ie. NullRouter protected IRouter Router => ActionContext.RouteData.Routers[0];
  3. This causes ArgumentOutOfRangeException while using UrlHelper

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core ‘2.2’
  2. Run this code:

Generate new WebApi project In Startup.cs

public void ConfigureServices(IServiceCollection services)
{
	services.AddRouting(opt => opt.LowercaseUrls = true);

	
	services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
	services.AddScoped<IUrlHelper>(implementationFactory =>
	{
		var actionContext = implementationFactory.GetService<IActionContextAccessor>().ActionContext;
		return new UrlHelper(actionContext);
	});

	services.AddMvc()
		.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

TestController.cs

using Microsoft.AspNetCore.Mvc;

[Route("api/test")]
public class TestController : Controller
{

    protected IUrlHelper UrlHelper {get;set;}

	public TestController(IUrlHelper urlHelper)
	{
        UrlHelper = urlHelper;
	}

	[HttpGet]
	public IActionResult ListAsync() 
	{
		var res = UrlHelper.Action(nameof(ListAsync));
		return Ok(res);
	}
}
  1. Execute action GET /api/test
  2. See error:
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.UrlHelperExtensions.Action(IUrlHelper helper, string action)
TestController.ListAsync() in TestController.cs
		var res = UrlHelper.Action(nameof(ListAsync));
  1. Switch CompabilityVersion to CompatibilityVersion.Version_2_1
  2. Watch action run as expected

Expected behavior

Action should return /api/test No ArgumentOutOfRangeException should be thrown

Additional context

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 17 (8 by maintainers)

Most upvoted comments

UrlHelper relies on IRouter and will not work when endpoint routing is enabled. There is an EndpointRoutingUrlHelper that is used for backwards compatibility reasons but is internal.

Instead of injecting UrlHelper, you should change your code to use LinkGenerator. It is designed to be resolved from DI and is registered by default in 2.2

Use LinkGenerator.GetPathByAction: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.2#url-generation

@rynowak Do you think we should improve the exception UrlHelper throws when there is no router? Something like: “No router could be found. Use LinkGenerator instead.”