【问题标题】:Handling Brochure-style pages in ASP.Net MVC在 ASP.Net MVC 中处理小册子样式的页面
【发布时间】:2013-02-18 21:32:43
【问题描述】:

我已经建立了许多 ASP.Net MVC 站点,每个站点中都有许多适合 MVC 控制器和操作的页面,以及一些实际上只是小册子页面的页面 - /why、/why /ouradvantage、/about 等 - 没有真正功能的页面,只是一个视图,也许是一个布局,仅此而已。

对于这些小册子风格的页面,我真的更喜欢只有视图和一个好的路线来找到它,所以我可以把 /why 放在 /Brochure/Why.cshtml 或 /Brochure/Why/Index.cshtml无论哪种方式,它都会被很好地捡起。我想避免使用愚蠢的控制器和动作(就像我过去所做的那样)来处理这组 URL 和页面。

如何在 ASP.Net MVC 项目中解决这个问题?这一定是一个共同的需求。

编辑:我如何以详细方式执行此操作的示例:

我可以使用标准的 MVC 路由 ({controller}/{action}/{id}) 并发送一堆无用的控制器来获取我想要的 URL 和页面集。每次我想添加一个小册子样式(没有功能,只是一个视图)页面时,我都会添加一个控制器或动作,如下所示:

为什么选择控制器:

public class WhyController : Controller
{
    public ViewResult Index()
    {
        return View();
    }

    public ViewResult OurAdvantage()
    {
        return View();
    }
}

这会在 /why 和 /why/ouradvantage 放置一个视图 - 干净的 URL。如果我想要一个 /about 页面,我可以添加另一个控制器,它只返回一个名为 AboutController 的视图。如果它有 5 个子页面,我可以向那个控制器添加 5 个动作,所有这些动作都只返回一个视图。

如果 MVC 站点中的这些小册子式页面总计 100 页,那么我将有相当多的无用的控制器和动作,它们实际上什么都不做。不是很干。我对一种方法很感兴趣,只需将视图放在我的项目中的一个文件夹中,并让它们可以访问(通过约定配置),在 /why 和 /why/ouradvantage 等干净的 URL 处。

有几种方法可以让我接近:

我可以放入一堆 .cshtml 页面并直接访问它们 - 但是我必须在 URL 中包含文件扩展名,并且视图文件本身必须位于根目录中。

我可以使用 ASP.Net 区域为这些区域定义一个区域,但是所有小册子样式的页面都必须位于至少一个 URL 段深处,并且我仍然遇到上述 URL 中文件扩展名的问题。

我可以定义许多疯狂的路线。

我怀疑这经常出现在具有少量或大量此类小册子样式页面的 MVC 项目中 - 似乎应该有一种干净的方法来做到这一点。

编辑:垃圾邮件路由引擎的蹩脚解决方案。

创建一个映射路由的类,例如:

public static void MapRoutes(RouteCollection routes, string appRoot, string path)
{
    if (!path.Contains("~/"))
        throw new NotSupportedException("Pages path must be virtual (use ~/ syntax).");

    var physicalPath = appRoot + path.Substring(2).Replace("/", "\\");

    var dir = new DirectoryInfo(physicalPath);
    var pages = dir.GetFiles("*.cshtml", SearchOption.AllDirectories);

    int rootLength = appRoot.Length;
    var rootParsed = pages.Select(p => "~/" + p.FullName.Substring(rootLength).Replace("\\", "/"));
    int folderPathLength = path.Length + 1;

    var mapped = rootParsed.Select(p => new {
        Url = p.Substring(folderPathLength, p.Length - 7 - folderPathLength),
        File = p
    });

    var routedPages = mapped.Select(m => routes.MapRoute(
        name: m.Url,
        url: "{*url}",
        defaults: new { path = m.File, Controller = "Brochure", Action = "Catchall" },
        constraints: new { url = m.Url }
    )).ToArray();
}

你可以在 RouteConfig 中这样调用它:

BrochureRoute.MapRoutes(routes, Server.MapPath("~/"), "~/Brochure");

这显然将所有这些页面映射到 BrochureController,您也需要它:

public class BrochureController : Controller
{
    public ViewResult Catchall(string path)
    {
        return View(path);
    }
}

2 个问题:

  1. 正如我所提到的,它会向路由引擎发送垃圾邮件 - 如果您有 100 个页面,那么您就有 100 个路由。
  2. 通过上述路径似乎会扰乱正常的 Razor 管道 - 以这种方式访问​​页面会出现如下错误:

'~/Brochure/About.cshtml' 处的视图必须派生自 WebViewPage

【问题讨论】:

  • 我不完全理解您想要的行为...请在您的帖子中提供更多路线示例和结果操作
  • @DaveA 更新为包含更清晰的代码示例。
  • 我想我明白了。您愿意使用可互换的零件吗?这你可以有一个传递部分名称的路线。美妙之处在于您的路线仍然看起来不错,并且您将拥有多个控制器,每个控制器都有一个视图,并且每个控制器的专用视图文件夹中有无数的部分......

标签: asp.net-mvc razor routing asp.net-mvc-routing


【解决方案1】:

可以使用RouteCollection.MapPageRoute方法来映射单个页面。

routes.MapPageRoute("", "why", "~/Brochure/Why.aspx");

当然,您可以使用 Razor 视图引擎 (.cshtml) 代替 WebForms。

Check MSDN documentation for some more details.


更新 2

你是对的,你不能将它用于 .cshtml 页面。但是,您不需要使用路由来访问网页(.cshtml 文件)。创建文件并省略 URL 中的扩展名就足够了。要实现您想要的结构,您可以这样做:

  • 您的 Web 项目必须允许网页呈现。要启用此功能,请转到 web.config 并将 webpages.Enabled 设置为 true。 (<add key="webpages:Enabled" value="true" />)
  • 在您的 Web 应用程序根目录中创建文件夹 why
  • 添加名为 Index.cshtml 的 MVC 视图页面。这可以从http://yoursite.com/why 访问
  • 添加名为 ouradvantages.cshtml 的 MVC ViewPage。这可以从http://yoursite.com/why/ouradvantages 访问
  • 您还可以使用 @UrlData 集合访问 url 数据

查看有关此here 的更多信息。更多关于网页的一般信息is also available on asp.net website

如果出于某种原因您确实需要使用路由,则需要自定义 RouteHandler。你可以find one implemented on Nuget。这个包的使用例子是here


更新

如果您想避免手动添加每条路线,您的选择就很少。

1) 创建一个约定来识别小册子页面

您可以通过以下方式做到这一点:默认情况下 URL 是小册子页面,并将“非小册子”页面隔离到特定部分:

routes.MapPageRoute("Default", "{brochurepage}", "~/Brochure/{brochurepage}.aspx");
// isolate non-brochure pages to "site" section
routes.MapRoute(
    "",
    "site/{controller}/{action}/{id}",                      // URL with parameters
    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
);

2) 在集合中保存小册子页面名称

List<string> brochurePages = new List<string>()
{ "about", "why", "contact" }; // add more pages here
....
foreach (var brochurePage in brochurePages)
    routes.MapPageRoute("", brochurePage, "~/Brochure/" + brochurePage + ".aspx");

对于您在问题中提到的两个 URL,我必须非常具体。要为“/why”和“/why/ouradvantage”实现路由,您将无法编写优雅的代码。这需要特殊处理。

【讨论】:

  • 确实,但是我需要为每个小册子样式的页面做 MapPageRoute - 不是很干。如果我有 100 个小册子样式的页面,我必须继续采取这个额外的步骤,将这些 MapPageRoute 调用添加到 RouteConfig.cs。这也可能对路由引擎的性能没有帮助。
  • 关键是将您引导到可用于执行此操作的 ASP.NET 扩展点。它很干,因为我的示例是单页。对于多页,要么 1) 提出一个标识小册子页面的约定,要么 2) 创建一个页面名称集合并在循环中调用 MapPageRoute。我会用一个例子来扩展我的答案
  • 距离越来越近了;您提到的集合可以像 Directory.FindFiles(Server.MapMath("~/Brochure"), "*.cshtml") 一样生成。如果对 MapPageRoute 的重复调用只是扩展一个简单的哈希表,则性能影响可以忽略不计。然而,您对子页面的注释成为一个问题,允许 ~\why\index.cshtml 或 ~\why.cshtml 的简单性也是如此。不过更近了。
  • 如果您希望得到比这更具体的答案,您必须更具体地了解您希望实施的约定类型。有可能解决“/why”、“/why/ouradvantages”被视为两页的问题,但这将涉及根据添加路由的顺序了解哪个路由引擎给出的优先级。不难发现,但我认为这是有风险的,因为它可能会在 ASP.NET 的未来版本中发生变化。
  • 我认为,如果我可以在 RouteConfig 中使用一些代码连接映射,您的解决方案就会做到这一点 - 尽管事实证明 MapPageRoute 不能按原样使用 .cshtml 文件,所以我需要弄清楚如何让那一半先发挥作用。
【解决方案2】:

如果您的问题是您不想为所有这些“愚蠢”页面使用单独的控制器,我可以建议您创建一种逻辑方式将它们路由到单个控制器,然后呈现正确的数据/基于视图在路线上?

例如,您的网址可以读取“/Pages/Why/About”,您可以将其路由到您的页面控制器,它会接收“Why”和“About”作为参数,您可以相应地呈现您的内容。这有意义吗?

【讨论】:

  • 这是我过去采用的那种 hacky 感觉的方法,我试图避免这种方法,因为似乎任何 MVC 项目都可能需要这些类型的页面,并且应该将它们连接起来微不足道。如果它们是 .aspx 页面,它似乎是微不足道的,但如果你想使用 Razor 语法 - 它需要这种混乱。我将通过代码示例发布更长的答案。
  • 我已经更新了我的问题以包含此类解决方案的示例 - 不幸的是,路由引擎可能太垃圾了,没有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
相关资源
最近更新 更多