从区域重定向到控制器/视图不在区域中

时间:2012-06-19 19:10:51

标签: asp.net-mvc-3 asp.net-mvc-routing asp.net-mvc-areas

我正在尝试从我的管理区域内部进行错误重定向到我的错误视图,这是在所有区域之外共享的。

我在Lobby控制器上创建了一个名为error的方法:

    public ViewResult Error()
    {
         return View("Views/Shared/Error");
    }

视图存在于Views / Shared目录中,称为Error。

在管理区域的我的用户控制器中,我正在尝试:

    return RedirectToAction("Error", "Lobby", new {area = ""});

但是,应用程序尝试重定向到“/ Admin / Lobby / Error”,尽管我在路由值中有area =“”。当我尝试单步执行代码时,在上一行和浏览器中的404错误之间没有执行任何行。

我在这里缺少什么?

编辑补充:实际上我的路线可能有问题。我尝试在登录后更改默认行为,直接指向不在某个区域的控制器,它会自动尝试将我发送到我的管理区域。

我正在努力实现的目标:我希望将具有区域的路由解释为需要转到该区域,并且希望没有区域的路由使用默认区域。目前我只有一个区域(管理员),但会有更多。

以下是我的路线:

routes.MapRoute("ErrorPage", "Error", new { controller="Error", action="Error"  },    new[] { "WebUI.Controllers" }); //Error Route

        routes.MapRoute(
            "", // Route name
            "Admin/{controller}/{action}/{id}", // URL with parameters
            new { area ="Admin", controller = "Client", action = "ManageClients", id = UrlParameter.Optional } // Parameter defaults
            , new[] { "WebUI.Areas.Admin.Controllers" } //prioritize admin
        );

        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Lobby", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            , new[] {"WebUI.Controllers"} //prioritize main
        );
IIRC当我尝试使用没有“硬编码”“路径”的路由作为区域名称时,路由引擎尝试将没有区域的路由填充到区域路由中,导致控制器被误认为是区域和控制器的动作。

但是我怀疑它现在正在做什么以某种方式将“/ Admin”附加到我的所有路线上! 例如,应用程序在启动时首先进入帐户/登录(不在某个区域)。当我更改登录行为时转到:

        return RedirectToAction("Summary", "Logging");

正在形成一个url / Admin / Logging / Summary而不是/ Logging / Summary。 即使我手动将其更改为

  return RedirectToAction("Summary", "Logging", new {area=""});

必须从路线中取“默认”管理区域。但是,如果我从路线中删除默认区域,那么我的管理员会链接所有中断。

这是我的区域注册:

    public override void RegisterArea(AreaRegistrationContext context)
    {
        context.MapRoute(
            "Admin_default",
            "Admin/{controller}/{action}/{id}",
            new { action = "Index", id = UrlParameter.Optional },
         new [] { "WebUI.Areas.Admin.Controllers" }
        );
    }

所以我想我需要将我的问题改为:我如何配置区域和路线以便路线&区域内外的链接是否正常运行?

1 个答案:

答案 0 :(得分:0)

我最终通过修改Phil Haack的AreaRouteHelper扩展来解决问题:

public static class AreaRouteHelper
{
    public static void MapAreas(this RouteCollection routes, string url, string rootNamespace, string[] areas)
    {
        Array.ForEach(areas, area =>
        {
            Route route = new Route("{area}/" + url, new MvcRouteHandler());
            route.Constraints = new RouteValueDictionary(new { area });
            string areaNamespace = rootNamespace + ".Areas." + area + ".Controllers";
            route.DataTokens = new RouteValueDictionary(new { namespaces = new string[] { areaNamespace } });
            route.Defaults = new RouteValueDictionary(new { action = "Index", controller = "Landing", id = "" });
            routes.Add(route);
        });
    }

    public static void MapRootArea(this RouteCollection routes, string url, string rootNamespace, object defaults)
    {
        Route route = new Route(url, new MvcRouteHandler());
        route.DataTokens = new RouteValueDictionary(new { namespaces = new string[] { rootNamespace + ".Controllers" } });
        route.Defaults = new RouteValueDictionary(new { area = "", action = "Index", controller = "Lobby", id = "" });
        routes.Add(route);
    }
}

(基本上我必须从默认的RootArea默认值中删除“root”一词,并将默认值更改为“Lobby”和“Landing”,这是我们的标准而不是Home。当我在那里留下默认的“root”时,它将“root”添加到文件夹结构中,但不起作用)

然后我从RegisterRoutes中删除了后两条路线并替换为:

 routes.MapAreas("{controller}/{action}/{id}", "WebUI", new[] { "Admin", "Analysis" });

        routes.MapRootArea("{controller}/{action}/{id}",
            "WebUI",
            new { controller = "Lobby", action = "Index", id = "" });

此修复程序需要区域感知视图引擎。 Haack项目中的一个是一个1.0 webform视图引擎,我已经有了一个区域感知的剃刀视图引擎,我从某个地方的另一个帖子中抓取了这个工作。老实说,我不记得我发现它的帖子,或者我直接链接到它以表示作者。但这就是它的样子(如果有人在这里知道是谁写的,我会高兴地将它归于那个人)。请注意,这里的映射与我的项目相匹配,如果没有一些调整,可能与其他人没有匹配!

 public class RazorAreaAwareViewEngine :RazorViewEngine
{
    private static readonly string[] EmptyLocations = { };

    public RazorAreaAwareViewEngine()
    {
        MasterLocationFormats = new string[]
                                    {
                                        "~/Views/{1}/{0}.master",
                                        "~/Views/Shared/{0}.master"
                                    };
        ViewLocationFormats = new string[]
                                  {
                                      "~/Areas/{2}/Views/{1}/{0}.cshtml",
                                      "~/Areas/{2}/Views/Shared/{0}.cshtml",
                                      "~/{2}/{0}.cshtml",
                                      "~/Views/{1}/{0}.cshtml",
                                      "~/Views/Shared/{0}.cshtml",
                                  };
        PartialViewLocationFormats = ViewLocationFormats;
    }
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName,string masterName, bool useCache)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(viewName))
        {
            throw new ArgumentNullException(viewName,
                "Value cannot be null or empty.");
        }
        string area = GetArea(controllerContext);

        return FindAreaView(controllerContext, area, viewName,
            masterName, useCache);
    }

    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName,bool useCache)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(partialViewName))
        {
            throw new ArgumentNullException(partialViewName,
                "Value cannot be null or empty.");
        }
        string area = GetArea(controllerContext);

        return FindAreaPartialView(controllerContext, area,
            partialViewName, useCache);
    }

    protected string GetArea(ControllerContext controllerContext)
    {
        object area = null;
        if (controllerContext.RouteData.DataTokens.ContainsKey("area"))
        {
            area = controllerContext.RouteData.DataTokens["area"];
        }
        if (area == null)
        {
            controllerContext.RouteData.Values.TryGetValue("area", out area);
        }
        if(area !=null)
        {
            return area.ToString();
        }
        return null;
    }

    protected virtual ViewEngineResult FindAreaView(ControllerContext controllerContext, string areaName, string viewName,string masterName, bool useCache)
    {
        string controllerName =
            controllerContext.RouteData.GetRequiredString("controller");
        string[] searchedViewPaths;
        string viewPath = GetPath(controllerContext, ViewLocationFormats,
            "ViewLocationFormats", viewName, controllerName, areaName, "View",
            useCache, out searchedViewPaths);
        string[] searchedMasterPaths;
        string masterPath = GetPath(controllerContext, MasterLocationFormats,
            "MasterLocationFormats", masterName, controllerName, areaName,
            "Master", useCache, out searchedMasterPaths);
        if (!string.IsNullOrEmpty(viewPath) &&
            (!string.IsNullOrEmpty(masterPath) ||
              string.IsNullOrEmpty(masterName)))
        {
            return new ViewEngineResult(CreateView(controllerContext, viewPath,
                masterPath), this);
        }
        return new ViewEngineResult(
            searchedViewPaths.Union<string>(searchedMasterPaths));
    }

    protected virtual ViewEngineResult FindAreaPartialView(ControllerContext controllerContext, string areaName,string viewName, bool useCache)
    {
        string controllerName =
            controllerContext.RouteData.GetRequiredString("controller");
        string[] searchedViewPaths;
        string partialViewPath = GetPath(controllerContext,
            ViewLocationFormats, "PartialViewLocationFormats", viewName,
            controllerName, areaName, "Partial", useCache,
            out searchedViewPaths);
        if (!string.IsNullOrEmpty(partialViewPath))
        {
            return new ViewEngineResult(CreatePartialView(controllerContext,
                partialViewPath), this);
        }
        return new ViewEngineResult(searchedViewPaths);
    }

    protected string CreateCacheKey(string prefix, string name,
        string controller, string area)
    {
        return string.Format(CultureInfo.InvariantCulture,
            ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:",
            base.GetType().AssemblyQualifiedName,
            prefix, name, controller, area);
    }

    protected string GetPath(ControllerContext controllerContext,string[] locations, string locationsPropertyName, string name,
        string controllerName, string areaName, string cacheKeyPrefix,bool useCache, out string[] searchedLocations)
    {
        searchedLocations = EmptyLocations;
        if (string.IsNullOrEmpty(name))
        {
            return string.Empty;
        }
        if ((locations == null) || (locations.Length == 0))
        {
            throw new InvalidOperationException(string.Format("The property " +
                "'{0}' cannot be null or empty.", locationsPropertyName));
        }
        bool isSpecificPath = IsSpecificPath(name);
        string key = CreateCacheKey(cacheKeyPrefix, name,
            isSpecificPath ? string.Empty : controllerName,
            isSpecificPath ? string.Empty : areaName);
        if (useCache)
        {
            string viewLocation = ViewLocationCache.GetViewLocation(
                controllerContext.HttpContext, key);
            if (viewLocation != null)
            {
                return viewLocation;
            }
        }
        if (!isSpecificPath)
        {
            return GetPathFromGeneralName(controllerContext, locations, name,
                controllerName, areaName, key, ref searchedLocations);
        }
        return GetPathFromSpecificName(controllerContext, name, key,
            ref searchedLocations);
    }

    protected string GetPathFromGeneralName(ControllerContext controllerContext,string[] locations, string name, string controllerName,
        string areaName, string cacheKey, ref string[] searchedLocations)
    {
        string virtualPath = string.Empty;
        searchedLocations = new string[locations.Length];
        for (int i = 0; i < locations.Length; i++)
        {
            if (string.IsNullOrEmpty(areaName) && locations[i].Contains("{2}"))
            {
                continue;
            }
            string testPath = string.Format(CultureInfo.InvariantCulture,
                locations[i], name, controllerName, areaName);
            if (FileExists(controllerContext, testPath))
            {
                searchedLocations = EmptyLocations;
                virtualPath = testPath;
                ViewLocationCache.InsertViewLocation(
                    controllerContext.HttpContext, cacheKey, virtualPath);
                return virtualPath;
            }
            searchedLocations[i] = testPath;
        }
        return virtualPath;
    }

    protected string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey,ref string[] searchedLocations)
    {
        string virtualPath = name;
        if (!FileExists(controllerContext, name))
        {
            virtualPath = string.Empty;
            searchedLocations = new string[] { name };
        }
        ViewLocationCache.InsertViewLocation(controllerContext.HttpContext,
            cacheKey, virtualPath);
        return virtualPath;
    }

    protected static bool IsSpecificPath(string name)
    {
        char ch = name[0];
        if (ch != '~')
        {
            return (ch == '/');
        }
        return true;
    }


}

进行这些更改后,内部和外部区域的动作链接都生成正确,我可以在内部和外部进行路由。外面的区域。