This question is locked. New answers and comments are not allowed.
I ran into an issue after upgrading to the latest version of the Telerik MVC Extensions. I'm developing a multi-tenant MVC4 application that uses a custom filter and routes to discriminate between tenants in the form {tenantId}/{controller}/{action}/{id} where tenantId is a required guid. I am also running the application locally as a virtual application (i.e. http:\\localhost\web).
After some searching I found reports of similar issues reported in Q2 (Another ControllerAuthorization.IsAccessibleToUser error in Q2 2012) when using areas. I dug around in the source and found some very strange logic that I assume was put into place to solve the bug with Areas:
Telerik.Web.Mvc/Infrastructure/Implementation/AuthorizationContextCache.cs (ln. 35):
Telerik.Web.Mvc/Infrastructure/Implementation/AuthorizationContextCache.cs (ln. 107):
Telerik.Web.MVC\Infrastructure\Implementation\RouteDataCache.cs (ln. 48):
Now I'm sure that the intention was to generate a url using the UrlHelper, and then somehow parse the url to find the route data from the route tables. However, .Action() as used above "Generates a fully qualified URL to an action method by using the specified action name and route values" which includes the virtual application path. Trying to look up the route from a URL that includes the virtual application path will in nearly every scenario return the wrong route. It gets worse when the route contains a non-string (read: integer, guid, etc.) because the cast will fail when attempting to parse out the route values.
The internal implementation of InternalHttpRequestBase is incorrect and incomplete (and a pretty back kludge). The AppRelativeCurrentExecutionFilePath property should return a relative URL from the application root, including the leading "~/". The very first line in the Route.GetRouteData is:
so the route data is guaranteed to be incorrect even if you don't happen to be running in a virtual application. If you are running in a virtual application, then the AppRelativeCurrentExecutionFilePath property would still be incorrect even if with the "~" character. With the above behavior, there is virtually no way that the NavigationItemAuthorization functionality actually works, even in a simple scenario.
After some searching I found reports of similar issues reported in Q2 (Another ControllerAuthorization.IsAccessibleToUser error in Q2 2012) when using areas. I dug around in the source and found some very strange logic that I assume was put into place to solve the bug with Areas:
Telerik.Web.Mvc/Infrastructure/Implementation/AuthorizationContextCache.cs (ln. 35):
#if MVC3 if (requestContext == null) { requestContext = new RequestContext(request.HttpContext, request.RouteData); } else { requestContext.HttpContext = request.HttpContext; request.RouteData = request.RouteData; }#else requestContext = new RequestContext(request.HttpContext, request.RouteData);#endif if (routeValues != null && routeValues.TryGetValue("Area", out area)) { areaName = area.ToString(); key = areaName + " " + key; }#if MVC3 if (urlHelper == null) { urlHelper = new UrlHelper(requestContext); } requestContext.RouteData = routeDataCache.GetRouteData(key, GenerateURL(actionName, controllerName, routeValues)) ?? requestContext.RouteData;#endifTelerik.Web.Mvc/Infrastructure/Implementation/AuthorizationContextCache.cs (ln. 107):
#if MVC3 private string GenerateURL(string actionName, string controllerName, RouteValueDictionary routeValues) { var url = urlHelper.Action(actionName, controllerName, routeValues); if (url == "/" && !string.IsNullOrEmpty(controllerName) && !string.IsNullOrEmpty(actionName)) { url = string.Format("{0}{1}/{2}", url, controllerName, actionName); } return url; }#endifTelerik.Web.MVC\Infrastructure\Implementation\RouteDataCache.cs (ln. 48):
internal class InternalHttpRequestBase : HttpRequestBase{ private readonly string appRelativeUrl; public InternalHttpRequestBase(string appRelativeUrl) { this.appRelativeUrl = appRelativeUrl; } public override string AppRelativeCurrentExecutionFilePath { get { return appRelativeUrl; } } public override string PathInfo { get { return ""; } }}Now I'm sure that the intention was to generate a url using the UrlHelper, and then somehow parse the url to find the route data from the route tables. However, .Action() as used above "Generates a fully qualified URL to an action method by using the specified action name and route values" which includes the virtual application path. Trying to look up the route from a URL that includes the virtual application path will in nearly every scenario return the wrong route. It gets worse when the route contains a non-string (read: integer, guid, etc.) because the cast will fail when attempting to parse out the route values.
The internal implementation of InternalHttpRequestBase is incorrect and incomplete (and a pretty back kludge). The AppRelativeCurrentExecutionFilePath property should return a relative URL from the application root, including the leading "~/". The very first line in the Route.GetRouteData is:
string str = string.Concat(httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2), httpContext.Request.PathInfo);