0

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

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

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

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

从管理区域中的用户控制器,我正在尝试执行以下操作:

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

但是,尽管我在路由值中有 area="",但应用程序仍试图重定向到“/Admin/Lobby/Error”。当我尝试单步执行代码时,在上一行和浏览器中的 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”作为区域名称的路由时,路由引擎试图将没有区域的路由填充到区域路由中,导致控制器被误认为是区域和控制器的动作。

但是我怀疑它现在正在做的是以某种方式将“/ 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" }
        );
    }

所以我想我需要将我的问题重新表述为:如何配置区域和路线,以便区域内外的路线和链接正常运行?

4

1 回答 1

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;
    }


}

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

于 2012-06-20T22:44:17.037 回答