如何设置MVC缺省路由(到一个区域) [英] How to set a Default Route (To an Area) in MVC

查看:278
本文介绍了如何设置MVC缺省路由(到一个区域)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

确定这已被问过,但没有固溶在那里。因此,对于自己和他人的目的,谁可能会发现这个有用。

Ok this has been asked before but there is no solid solution out there. So for purpose of myself and others who may find this useful.

在MVC2(ASP.NET)我想它所以当有人浏览到该网站,没有指定的默认​​区域。所以,导航到我的网站应该送你到ControllerX ActionY在AreaZ。

In MVC2 (ASP.NET) I want it so when someone navigates to the website, there is a default area specified. So navigating to my site should send you to ControllerX ActionY in AreaZ.

在Global.asax使用下面的路由

Using the following route in the Global.asax

routes.MapRoute(
                "Area",
                "",
                new { area = "AreaZ", controller = "ControllerX ", action = "ActionY " }
            );

现在这个工程作为它试图将正确的页面。但是MVC继续寻找在站点的根目录,而不是在区域文件夹的视图。

Now this works as in it does try to serve the correct page. However MVC proceeds to look for the View in the root of the site and not in the Area folder.

有没有解决这个办法吗?

Is there a way to resolve this?

修改

有一个解决方案,这是在ControllerX,ActionY返回该视图的完整路径。一个黑客位,但它确实工作。但是我希望有一个更好的解决方案。

There is a 'Solution' and that is in ControllerX, ActionY return the full path of the view. Bit of a hack but it does work. However I'm hoping there is a better solution.

         public ActionResult ActionY()
        {
            return View("~/Areas/AreaZ/views/ActionY.aspx");
        }

编辑:

此具有网页的HTML的ActionLink时也成为一个问题。如果该区域未设置跳转链接输出空白。

This also becomes an issue when having a HTML ActionLink of the page. If the area is not set the Action Link is output blank.

是这一切的设计还是缺陷?

Is all of this by design or a flaw?

推荐答案

这一次让我感兴趣的,我终于有机会寻找到它。其他人显然不明白,这是是找到视图,而不是用问题路由的本身就是一个问题 - 这可能是因为你的问题的标题表明它是关于路由。

This one interested me, and I finally had a chance to look into it. Other folks apparently haven't understood that this is an issue with finding the view, not an issue with the routing itself - and that's probably because your question title indicates that it's about routing.

在任何情况下,因为这是一个查看相关的问题,只有这样,才能得到你想要的是替换默认的视图引擎即可。通常情况下,当你做到这一点,这对切换视图引擎(即星火,NHaml等)的目的很简单。在这种情况下,这不是我们需要覆盖的视图创建逻辑,但 FindPartialView FindView 中的方法 VirtualPathProviderViewEngine 类。

In any case, because this is a View-related issue, the only way to get what you want is to override the default view engine. Normally, when you do this, it's for the simple purpose of switching your view engine (i.e. to Spark, NHaml, etc.). In this case, it's not the View-creation logic we need to override, but the FindPartialView and FindView methods in the VirtualPathProviderViewEngine class.

您可以感谢你的幸运之星,这些方法其实都是虚拟的,因为一切都在 VirtualPathProviderViewEngine 甚至没有的访问的东西 - 它是私有的,这使得它的非常的烦人,因为你如果你想让它发挥的位置,高速缓存和位置不错的基本上重写一半这已经被写入了code的覆盖查找逻辑格式。在反射一些挖后,我终于想出一个工作的解决方案。

You can thank your lucky stars that these methods are in fact virtual, because everything else in the VirtualPathProviderViewEngine is not even accessible - it's private, and that makes it very annoying to override the find logic because you have to basically rewrite half of the code that's already been written if you want it to play nice with the location cache and the location formats. After some digging in Reflector I finally managed to come up with a working solution.

我在这里所做的是先创建一个抽象的 AreaAwareViewEngine 直接从 VirtualPathProviderViewEngine 的派生,而不是 WebFormViewEngine 。我这样做,这样,如果你想创建星火意见,而不是(或其他),你仍然可以使用这个类为基本类型。

What I've done here is to first create an abstract AreaAwareViewEngine that derives directly from VirtualPathProviderViewEngine instead of WebFormViewEngine. I did this so that if you want to create Spark views instead (or whatever), you can still use this class as the base type.

在$ C $下面c是pretty长篇大论,所以给你的东西,它实际上做了简单总结:它可以让​​你把一个 {2} 入位置的格式,其对应于区域的名称,以同样的方式 {1} 对应于控制器名称。而已!这就是我们不得不写这一切code为:

The code below is pretty long-winded, so to give you a quick summary of what it actually does: It lets you put a {2} into the location format, which corresponds to the area name, the same way {1} corresponds to the controller name. That's it! That's what we had to write all this code for:

public abstract class BaseAreaAwareViewEngine : VirtualPathProviderViewEngine
{
    private static readonly string[] EmptyLocations = { };

    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 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 string getArea(ControllerContext controllerContext)
    {
        // First try to get area from a RouteValue override, like one specified in the Defaults arg to a Route.
        object areaO;
        controllerContext.RouteData.Values.TryGetValue("area", out areaO);

        // If not specified, try to get it from the Controller's namespace
        if (areaO != null)
            return (string)areaO;

        string namespa = controllerContext.Controller.GetType().Namespace;
        int areaStart = namespa.IndexOf("Areas.");
        if (areaStart == -1)
            return null;

        areaStart += 6;
        int areaEnd = namespa.IndexOf('.', areaStart + 1);
        string area = namespa.Substring(areaStart, areaEnd - areaStart);
        return area;
    }

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

现在的规定,这不是一个具体的引擎,所以你必须创建一个为好。这部分,幸运的是,是的的容易,我们需要做的是设置默认格式和实际创建的意见:

Now as stated, this isn't a concrete engine, so you have to create that as well. This part, fortunately, is much easier, all we need to do is set the default formats and actually create the views:

public class AreaAwareViewEngine : BaseAreaAwareViewEngine
{
    public AreaAwareViewEngine()
    {
        MasterLocationFormats = new string[]
        {
            "~/Areas/{2}/Views/{1}/{0}.master",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.master",
            "~/Areas/{2}/Views/Shared/{0}.cshtml",
            "~/Views/{1}/{0}.master",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.master"
            "~/Views/Shared/{0}.cshtml"
        };
        ViewLocationFormats = new string[]
        {
            "~/Areas/{2}/Views/{1}/{0}.aspx",
            "~/Areas/{2}/Views/{1}/{0}.ascx",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.aspx",
            "~/Areas/{2}/Views/Shared/{0}.ascx",
            "~/Areas/{2}/Views/Shared/{0}.cshtml",
            "~/Views/{1}/{0}.aspx",
            "~/Views/{1}/{0}.ascx",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.aspx"
            "~/Views/Shared/{0}.ascx"
            "~/Views/Shared/{0}.cshtml"
        };
        PartialViewLocationFormats = ViewLocationFormats;
    }

    protected override IView CreatePartialView(
        ControllerContext controllerContext, string partialPath)
    {
        if (partialPath.EndsWith(".cshtml"))
            return new System.Web.Mvc.RazorView(controllerContext, partialPath, null, false, null);
        else
            return new WebFormView(controllerContext, partialPath);
    }

    protected override IView CreateView(ControllerContext controllerContext,
        string viewPath, string masterPath)
    {
        if (viewPath.EndsWith(".cshtml"))
            return new RazorView(controllerContext, viewPath, masterPath, false, null);
        else
            return new WebFormView(controllerContext, viewPath, masterPath);
    }
}

请注意,我们增加了一些条目标准 ViewLocationFormats 。这些都是新的 {2} 项,其中 {2} 将映射到地区我们把在的RouteData 。我已经离开了独 MasterLocationFormats ,但很明显,你可以改变,如果你想要的。

Note that we've added few entries to the standard ViewLocationFormats. These are the new {2} entries, where the {2} will be mapped to the area we put in the RouteData. I've left the MasterLocationFormats alone, but obviously you can change that if you want.

现在修改的Global.asax 来注册这个视图引擎:

Now modify your global.asax to register this view engine:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new AreaAwareViewEngine());
}

...和寄存器的默认路由:

...and register the default route:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        "Area",
        "",
        new { area = "AreaZ", controller = "Default", action = "ActionY" }
    );
    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = "" }
    );
}

现在创建 AreaController 我们只是引用:

Now Create the AreaController we just referenced:

public class DefaultController : Controller
{
    public ActionResult ActionY()
    {
        return View("TestView");
    }
}

显然,我们需要的目录结构和观点去用它 - 我们将保持这个超级简单:

Obviously we need the directory structure and view to go with it - we'll keep this super simple:

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<h2>TestView</h2>
This is a test view in AreaZ.

就是这样。 最后,我们就大功告成了

在大多数情况下,你应该能够只取 BaseAreaAwareViewEngine AreaAwareViewEngine ,把它放到任何MVC项目,所以即使它花了很多code来完成这件事,你只需要编写一次。在此之后,它只是一个在的global.asax.cs 编辑了几行,创建网站结构的问题。

For the most part, you should be able to just take the BaseAreaAwareViewEngine and AreaAwareViewEngine and drop it into any MVC project, so even though it took a lot of code to get this done, you only have to write it once. After that, it's just a matter of editing a few lines in global.asax.cs and creating your site structure.

这篇关于如何设置MVC缺省路由(到一个区域)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆