【问题标题】:How to catch all areas with MapRoute如何使用 MapRoute 捕捉所有区域
【发布时间】:2017-09-25 21:04:58
【问题描述】:

我是 MVC 新手并正在编辑现有应用程序。目前我在 RouteConfig.cs 中看到以下内容:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Util",
            "util/{action}",
            new {controller = "util"});

        routes.MapRoute(
            "Catchall",
            "{*url}",
            new {controller = "Main", action = "CatchUrl"});
    }
}

在主控制器内部有一个逻辑,它基本上执行RedirectToRoute 并将名为location 的区域、控制器、操作和查询字符串设置为某个值。

public class MainController : Controller
{
    public ActionResult CatchUrl()
    {
        var agencyId = 9;

        var routeValues = new RouteValueDictionary
        {
            {"area", "area1"},
            {"controller", "dashboard"},
            {"action", "index"},
            {"location", "testLocation"}
        };

        return RedirectToRoute(routeValues );
    }
}

这似乎工作正常,当你给它一个无效区域时,它会正确地转到默认区域。

我还看到了一个名为 CustomAreaRegistration.cs 的文件:

public abstract class CustomAreaRegistration : AreaRegistration
{
    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            AreaName + "Ajax",
            AreaName + "/{controller}/{action}",
            new { action = "Index" }
        );

        context.MapRoute(
            AreaName + "ShortUrl",
            AreaName + "/{controller}",
            new {action = "Index"}
            );
    }
}

我无法理解区域路由的工作原理以及它如何知道如何转到正确的控制器。

此外,我正在努力让您在访问时得到它

/{area}/ 它会执行一些逻辑并将您重定向到正确的控制器。类似于 CatchUrl 的工作原理

我的尝试:

    routes.MapRoute(
        "AreaIndex",
        "{module}/",
        new {controller = "Main", action = "Index"});

主控制器:

public class MainController : Controller
{
    public ActionResult Index()
    {
        var requestHost = HttpContext.Request.Url?.Host;
        var location= requestHost == "localhost" ? Request.QueryString["location"] : requestHost?.Split('.')[0];


        var routeValues = new RouteValueDictionary
        {
            {"area", ControllerContext.RouteData.Values["module"]},
            {"controller", "dashboard"},
            {"action", "index"},
            {"location", location}
        };

        return RedirectToRoute(routeValues );
    }

    public ActionResult CatchUrl()
    {
        var routeValues = new RouteValueDictionary
        {
            {"area", "area1"},
            {"controller", "dashboard"},
            {"action", "index"},
            {"location", "testLocation"}
        };

        return RedirectToRoute(routeValues );
    }
}

我明白了

路由表中没有路由与提供的值匹配。

我不确定为什么 CatchUrl 有效而我的无效。

【问题讨论】:

  • catch-all 的相关部分似乎是通配符组件{*url},我在您的{module}/ 路由中看不到任何类似的东西。或许可以看看下面的相关信息:stackoverflow.com/questions/7515644/…

标签: c# asp.net-mvc asp.net-mvc-routing


【解决方案1】:

我实际上不明白你在问什么,但仅查看代码,这不是在 MVC 3,4 和 5 中创建/使用 Areas 的标准方法。

您不需要在每个控制器中编写逻辑并进行重定向。

在我的RouteConfig 中,我通常只有默认路由映射。而当你对Areas有需求的时候,你可以在Visual Studio中右键单击MVC web项目,然后点击'Add -> Area'。这将在 Web 项目根目录下的 Areas 文件夹中创建一个区域名称为的文件夹。在区域文件夹中,您应该可以找到区域名称和映射的 AreaRegistration.cs

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "dashboard", action = "index", id = UrlParameter.Optional },
            namespaces: new[] { "Company.Project.Web.UI.Controllers" }
        );
    }
}

假设您要创建一个名为“Admin”的区域:

public class AdminAreaRegistration : AreaRegistration 
{
    public override string AreaName 
    {
        get 
        {
            return "admin";
        }
    }

    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            "admin_default",
            "admin/{controller}/{action}/{id}",
            new { action = "index", id = UrlParameter.Optional },
            namespaces: new[] { "Company.Project.Web.UI.Areas.Admin.Controllers" }
        );
    }
}

最后,我认为截图可能会有所帮助。

-- 更新--

基于cmets,如果你希望路由/apple?location=home去Apple Controller及其Home方法,而路由/orange?location=dashbard去Orange Controller及其Dashboard方法,最好在@中定义路由987654330@。

理想情况下,您希望您可以在RouteConfig 中拥有类似的内容:

        routes.MapRoute(
            name: "Area-CatchAll",
            url: "{area}?location={controller}"
        );

但这是不可能的,因为 MVC 会错误提示“路由 URL 不能以 '/' 或 '~' 字符开头并且不能包含 '?'字符。”。

相反,您可以将流量引导到控制器,并且可以将区域和位置定义为参数。

routes.MapRoute(
    name: "Area-CatchAll",
    url: "{area}",
    defaults: new { controller = "Area", action = "Translate" }
);

public class AreaController : Controller
{
    // The {area} from route template will be mapped to this area parameter.
    // The location query string will be mapped to this location parameter.

    public ActionResult Translate(string area, string location)
    {
        // This also assumes you have defined "index" method and
        // "dashboard" controller in each area.

        if (String.IsNullOrEmpty(location))
        {
            location = "dashboard";
        }
        return RedirectToAction("index", location, new { area = area });
    }
}

同样,如果我不需要的话,我不会创建这样的路线来将流量重定向到区域。

【讨论】:

  • 不允许我编辑:我已经有工作区域。我想要/area/ 路由到一个像控制器一样的包罗万象的控制器,这样就有机会弄清楚他们应该去哪个控制器。每个区域有两个主要控制器,仪表板和细节。相反,特定人员获取仪表板或详细信息取决于发送到/area/ 的获取参数/Apple/?location=foo 将转到Apple 区域和仪表板控制器,/Apple/?location=bar 将转到Apple 区域和详细信息控制器。举个例子。
  • 您可以为此创建路线。不需要控制器来处理这个
  • 看看我的更新,看看这是否是你想要完成的? @CuriousDeveloper
  • @DavidLiang - MapRoute 不能用于指定 URL 参数。但是,您可以通过继承 RouteBaseRoute 来实现。坦率地说,如果 CuriousDeveloper 真正追求的是重定向,我会为它创建一个全局过滤器,而不是创建一个仅用于重定向的无聊控制器。
【解决方案2】:

我正在努力得到它,以便当您访问时

/{area}/ 它会执行一些逻辑并将您重定向到正确的控制器。类似于 CatchUrl 的工作原理

首先,让我们明确这一点。包罗万象的路由不是路由,而是重定向。它向浏览器发出 HTTP 302 响应,告诉浏览器在您的服务器上查找新位置。这种事情效率不高,因为它需要通过网络进行额外的往返,但它是让浏览器中的 URL 从服务器端更改的唯一方法(如果您打算这样做)。

另一方面,路由接收初始请求并将其直接发送到特定资源(控制器操作),而无需额外的网络往返和无需更改浏览器中的 URL。

您的路线已经设置好使用

/Area/Controller

在这里路由到正确的控制器:

public abstract class CustomAreaRegistration : AreaRegistration
{
    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            AreaName + "Ajax",
            AreaName + "/{controller}/{action}",
            new { action = "Index" }
        );

        context.MapRoute(
            AreaName + "ShortUrl",
            AreaName + "/{controller}",
            new {action = "Index"}
        );
    }
}

(假设您对于设置AreaName 的每个区域都有一个 this 的子类)。

如果您确实想将/module/ URL 路由(而不是重定向)到相应区域中的DashboardController.Index 方法,则更改您的ShortUrl 以使controller 可选并将其默认为DashboardController .

public abstract class CustomAreaRegistration : AreaRegistration
{
    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            AreaName + "Ajax",
            AreaName + "/{controller}/{action}",
            new { action = "Index" }
        );

        context.MapRoute(
            AreaName + "ShortUrl",
            AreaName + "/{controller}",
            new { controller = "Dashboard", action = "Index" }
        );
    }
}

这会将 URL /Foo/ 发送到 Foo 区域的 DashboardController.Index 方法,但将 URL /Foo/Bar/ 发送到 Foo 区域的 BarController.Index 方法。

以上假设您足够勤奋,以确保您的AreaNames 中没有一个与您项目的非区域部分中的控制器名称相同。如果是这样,如果没有一些额外的配置(例如路由约束),您将永远无法访问那些非区域控制器。

根本不需要MainController.Index 方法(除非您有特定原因要更改浏览器中的 URL)。

【讨论】:

  • 我已经有工作区了。我想要/area/ 路由到一个像控制器一样的包罗万象的控制器,这样就有机会弄清楚他们应该去哪个控制器。每个区域有两个主要控制器,仪表板和细节。相反,特定人员获取仪表板或详细信息取决于发送到/area/ 的获取参数/Apple/?location=foo 将转到Apple 区域和仪表板控制器,/Apple/?location=bar 将转到Apple 区域和详细信息控制器。举个例子。
  • 那么为什么不直接路由而不是重定向呢?出于某种原因,您是否需要将/Apple/?location=foo 更改为/Apple/Foo
  • location= 是某些用户标识符的持有者,它重定向到启动屏幕或不基于该用户存储在数据库中的属性。所以它不是指一个控制器,而是指向一个可以用来找到重定向到哪个控制器的东西
  • 您不应该仅仅依赖于请求查询字符串上的用户标识符。这将不安全,因为登录用户可以轻松更改为其他人。相反,您应该在服务器端获取登录的用户身份,并根据用户的属性将请求重定向到其他区域。
  • 谢谢大卫。 CuriousDeveloper 说“用户”,而不是“登录用户”。这里没有足够的特异性来确定他真正想要什么。重定向不利于 SEO 和性能,更不用说如果您 POST 到 URL 并且重定向到 GET,那么 POST 的数据就会丢失。但如果他试图创建某种“短 URL”重定向服务,例如 goo.gl,这可能是正确的方法。
猜你喜欢
  • 2015-07-06
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 2018-10-30
  • 2019-03-22
  • 2011-11-16
  • 2014-06-22
  • 1970-01-01
相关资源
最近更新 更多