博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
004_URL 路由 - 定制路由系统 & 使用区域
阅读量:5034 次
发布时间:2019-06-12

本文共 10428 字,大约阅读时间需要 34 分钟。

定制路由系统

   路由系统是灵活可配置的,当然还可以通过下面这两种方式定制路由系统,来满足其他需求。

1、  通过创建自定义的RouteBase实现;

2、  通过创建自定义路由处理程序实现。

创建自定义的RouteBase实现

   创建自定义的RouteBase实现,需要实现一个RouteBase的派生类,而这需要实现以下两个方法:

  • GetRouteData(HttpContextBase httpContext):这是入站URL进行匹配的工作机制。框架依次对RouteTable.Routes的每个条目调用这个方法,直到其中之一返回一个非空值。
  • GetVirtualPath(RequestContext requestContext,RouteValueDictionary values):这是出站URL生成的工作机制。框架依次对RouteTable.Routes的每一个条目调用这个方法,直到其中之一返回一个非空值。

   为了演示这种自定义方式,这里创建了一个RouteBase的派生类。我们假设这样的一个需求环境:需要把一个现有的应用程序迁移到MVC框架,但不论出于什么原因,我们需要兼容之前的URL,那就可以通过这种方式来实现,当然可以通过规则的路由系统来处理——这里不对这种方式进行讨论。

         首先,创建一个处理旧式路由请求的控制器,将其命名为:LegacyController,如:

using System.Web.Mvc;namespace UrlsAndRoutes.Controllers{    ///     /// 用以处理旧式 URL 请求的控制器    ///     public class LegacyController : Controller    {        public ActionResult GetLegacyURL(string legacyURL)        {            // 应用程序迁移到 MVC 之前,请求是针对文件的,因此,实际上是需要在这里处理被请求的文件。但这里            // 只简单说明一下自定义 RouteBase 的实现原理,所以,此处仅在视图中显示这个 URL。            return View((object)legacyURL);        }    }}

         上面代码对View方法中的参数做了转换,如果不转换,则C#编译器会误认为要将参数作为要指定渲染的视图的名称的字符串(View方法的一个重载版本的实现)。下面是这个动作方法的视图GetLegacyURL.cshtml:

@model string@{    ViewBag.Title = "GetLegacyURL";    Layout = null;}

GetLegacyURL

The URL requested was:@Model

 

1、对输入URL进行路由

在Infrastructure文件夹中创建一个LegacyRoute类,其内容如下:

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using System.Web.Routing;namespace UrlsAndRoutes.Infrastructure{    public class LegacyRoute : RouteBase    {        private string[] urls;        public LegacyRoute(params string[] targetUrls)        {        }        public override RouteData GetRouteData(HttpContextBase httpContext)        {            RouteData result = null;            string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;            if (urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))            {                result = new RouteData(this, new MvcRouteHandler());                result.Values.Add("controller", "Legacy");                result.Values.Add("action", "GetLegacyURL");                result.Values.Add("legacyURL", "requestedURL");            }            return result;        }        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)        {            return null;        }    }}

   注册一条路由,以使其使用新建的这个RouteBase派生类:

public static void RegisterRoutes(RouteCollection routes)        {            // 注册自定义的 RouteBase 实现            routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library"));        }

2、生成输出URL

         在LegacyRoute中实现GetVirtualPath方法以使其能够支持输出URL的生成。如: 

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)        {            VirtualPathData result = null;            if (values.ContainsKey("legactURL") && urls.Contains((string)values["legacyURL"], StringComparer.OrdinalIgnoreCase))            {                // 如果存在一个匹配,将会创建一个 VirtualPathData 对象,在其中传递一个对当前对象的引用和出站 URL。由于路由系统已经预先将                // 字符“/”附加到了这个URL,因此,必须从生成的 URL 上删除这个前导字符。                result = new VirtualPathData(this, new UrlHelper(requestContext).Content((string)values["legacyURL"]).Substring(1));            }            return null;        }

   在ActionName.cshtml视图中添加下面这段代码,以使其能礼仪自定义路由生成输出URL:

@* 经由自定义路由生成一个输出 URL *@ This is a URL: @Html.ActionLink("Click me", "GetLegacyURL", new { legacyURL = "~/articles/Windows_3.1_Overview.html" })

         上面代码将产生一个这样的a元素:

<a href=”/articles/Windows_3.1_Overview.html”>Click me</a>

         用legacyURL属性创建的匿名类型被转换到了含有同名键的RouteValueDictionary类中。

创建自定义路由处理程序

   路由已经依赖这个MvcRouteHandler了,因为MvcRouteHandler把路由系统连接到了MVC框架。但通过实现IRouteHandler接口,路由系统仍允许自定义自己的路由处理程序,如下面的示例:

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Routing;namespace UrlsAndRoutes.Infrastructure{    public class CustomRouteHandler : IRouteHandler    {        public IHttpHandler GetHttpHandler(RequestContext requestContext)        {            return new CustomHttpHandler();        }    }    public class CustomHttpHandler : IHttpHandler    {        public bool IsReusable        {            get { return false; }        }        public void ProcessRequest(HttpContext context)        {            context.Response.Write("Hello");        }    }}

         IRouteHandler接口的目的是提供生成IHttpHandler接口的实现,且由它负责对请求进行处理。在该接口的MVC实现中,主要负责这几项工作:查找控制器、调用动作方法、渲染视图,并将结果写入到响应中。当然,这里的实现要简单的多,此处仅将单词“Hello”写到客户端,且只是文本形式。要想得到最终效果,需要在RouteConfig.cs文件中注册这个自定义处理程序:

public static void RegisterRoutes(RouteCollection routes)        {            // 注册自定义路由处理程序            routes.Add(new Route("SayHello", new CustomRouteHandler()));        }

 

使用区域

   MVC框架支持将Web应用程序组织成一些区域(Area),每个区域代表应用程序的一个功能端,如管理、结算、客户支持等等。这使得代码的管理很有用,尤其是大型项目,如果对所有控制器、视图和模型只使用一组文件夹,那将会是很难于管理的。

创建区域

   可以直接对项目右键,选择“添加”->“区域”进行添加。还可以在当前的区域中创建其他区域。在刚刚的操作之后,项目中将会出现如下这样的区域文件夹结构:

                       

         通过Areas/Admin文件夹,可以看出这是一个小型的MVC项目。其中有“Controllers”、“Models”和“Views”的文件夹。前两个是空的,但“Views”文件夹含有一个“Shared”文件夹和一个Web.config视图引擎配置文件(这里暂不对视图引擎进行讨论)。

         另外,这里还多了一个AdminAreaRegistration.cs文件,如:

using System.Web.Mvc;namespace UrlsAndRoutes.Areas.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 }            );        }    }}

 

         从清单中可以看出,该类中的RegisterArea方法注册了一个URL模式为Admin/{controller}/{action}/{id}的路由。当然,也可以在该方法中定义该区域专用的其他路由。

注意:如果要给路由赋名,必须确保这些名称在整个应用程序而不仅仅是某一区域中是唯一的。

   由于在Global.asax的Application_Start方法中已经对路由的注册进行过处理,因此,不需要在开发的过程中采取其他措施来确保该注册方法会被调用: 

public class MvcApplication : System.Web.HttpApplication    {        protected void Application_Start()        {            AreaRegistration.RegisterAllAreas();            WebApiConfig.Register(GlobalConfiguration.Configuration);            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);            RouteConfig.RegisterRoutes(RouteTable.Routes);            BundleConfig.RegisterBundles(BundleTable.Bundles);        }    }

         上面代码中对静态方法AreaRegistration.RegisterAllAreas的调用,会导致MVC框架对应用程序的所有类进行遍历,找出派生于AreaRegistration的所有类,并调用这些类上的RegisterArea方法。

注意:不用修改Application_Start方法中与路由相关的语句顺序。如果在AreaRegistration.RegisterAllAreas之前调用RegisterRoutes,那么会在区域路由之前定义路由。由于路由系统是按顺序评估的,这意味着对区域控制器的请求有可能会用不正确的路由进行匹配。

注:AreaRegistrationContext类中的MapRoute方法会自动把注册的路由限制到包含该区域控制器的命名空间。也就是说,当某区域创建控制器时,必须把它放在其默认的命名空间中;否则,路由系统将无法找到它。

填充区域

   在上一节“创建区域”一节中,已经知道在一个区域中可以创建控制器、视图以及模型等。下面,通过创建一个名为HomeController的控制器类,来演示应用程序中区域之间的分离:

   在下图中的Controllers文件夹中右键添加一个空的控制器:HomeController

 

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;namespace UrlsAndRoutes.Areas.Admin.Controllers{    public class HomeController : Controller    {        public ActionResult Index()        {            return View();        }    }}

         为了演示,对该控制器中Index动作方法右击,并添加相应的视图,添加后的视图将在:Areas/Admin/View/Home路径中。

 

视图内容如下:

@{    ViewBag.Title = "Index";    Layout = null;}    
Index

Admin Area Index

         从上面介绍可以看出,在一个区域内的工作方式与在一个MVC项目主区中工作是相当类似的。在项目中创建某个项的工作流也是相同的。其效果如下(导航路径:Admin/Home/Index):

 

解析不明确的控制器问题

   区域可能不像它们所展示的那样是自包含的。当一个区域被注册时,所定义的任何路由都被限制到与这个区域关联的命名空间之中。这也是能够请求/Admin/Home/Index,并得到WorkingWithAreas.Admin.Controllers命名空间中HomeController类的原因。

   然而,在RouteConfig.cs的RegisterRoutes方法中定义的路由却不受类似的限制。作为提醒,这里给出了示例应用程序此时的路由配置: 

public static void RegisterRoutes(RouteCollection routes)        {             routes.Add(new Route("SayHello", new CustomRouteHandler()));            routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library"));            routes.MapRoute("MyRoute", "{controller}/{action}");            routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" });        }

    名为“MyRoute”的路由把来自浏览器的输入URL转换为Home控制器上的Index动作。这时会收到一个错误的消息,因为没有为这条路由设置命名空间的约束,所以MVC框架会看到两个HomeController类。为了解决这一问题,需要在所有可能导致冲突的路由中,将主控制器命名空间列为优先,如:

public static void RegisterRoutes(RouteCollection routes)        {             routes.Add(new Route("SayHello", new CustomRouteHandler()));            routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library"));            routes.MapRoute("MyRoute", "{controller}/{action}",null,new[] {“UrlsAndRoutes.Controllers”});            routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" }, new[] {“UrlsAndRoutes.Controllers”});        }

         上面代码中加粗部分将项目控制器作为了优先。当然也可以对某个区域中的控制器实现优先。

生成对区域动作的链接

   对与同一区域中的动作,无需采取特殊的步骤来创建指向这些动作的链接。MVC框架会检测当前请求涉及的特定区域,然后出站URL生成将只在该区域定义的路由中查找一个匹配。如将下面代码添加到Admin区域的视图。

@Html.ActionLink("Click me", "About")

   会生成以下HTML:

<a href=”/Admin/Home/About”>Click me</a>

         为了对不同区域中的动作或根本无区域的动作创建一条链接,必须创建一个名为“area”的变量,并用它指定区域名,如:

@Html.ActionLink("Click me to go to another area", "Index", new { area = "Support" })

         因此,area被保留为片段变量名。假设创建了名为Support的区域,并有对应的标准路由定义,则将生成如HTML:

<a href=”/Support/Home”>Click me to go to another area</a>

如果想链接到顶级控制器(/Controllers文件夹中的一个控制器)上的一个动作,那么应该把area指定为空字符串,如:

    @Html.ActionLink("Click me to go to another area", "Index", new {

area = ""})

URL方案最佳做法

1、  使URL整洁和人性化

   下面摘抄一些生成友好URL的简单的纲要:

  • 设计URL来描述它们的内容,而不是应用程序的实现细节。使用/Articles/AnnualReport,而不是使用/Website_v2/CachedContentServer/FromCache/AnnualReport。
  • 尽可能采用内容标题而不是ID号,使用/Articles/AnnualReport,而不是/Articles/2392。如果必须使用一个ID号(以区别具有同样标题的条目或避免通过标题查找一个条目时,需要多余的数据库查询步骤),那么两者都有(如:/Articles/2392/AnnualReport)。这需要多打一些字符,但更要意义,并会改善搜索引擎排列。
  • 不用对HTML页面使用文件扩展名(如,.aspx或.mvc),但对特殊文件类型要用扩展名(如,.jpg、.pdf、.zip等)。如果是适当的设置了MIME类型,Web浏览器不会在意文件的扩展名,但人们却希望对PDF文件用.pdf扩展名。
  • 创建一种层次感(如:/Products/Menswear/Shirts/Red),这样,容易让人猜出父目录的URL。
  • 不区分大小写。ASP.NET路由系统默认是不区分大小写的。
  • 避免使用符合、代码和字符序列。需要用单词分隔符时,可以使用短横(如:/my-great-article)。下划线是不友好的,而URL编码的空格是奇特的(/my+great+article)或令人讨厌的(/my%20great%20article)。
  • 不用修改URL。打破链接等于失去商务。当确实需要修改URL时,通过永久重定向(301)尽可能长时间的继续支持旧式的URL方案。
  • 具有一致性。在整个应用程序中采用一种URL格式。URL应简短、易于输入、可剪辑(人性化可剪辑),且持久稳定,而且它们应该形象化网站结构。

2、GET和POST:选用正确的一个

   一般来说,GET请求应该被用于所有只读信息检索,而POST请求应该被用于各种修改应用程序状态的操作。用标准的术语说,GET请求用于安全交互(除信息检索外无其他影响),而POST请求用于不安全交互(作出决定或修改某些东西)。GET请求是可设定地址的——所有信息都包含在URL中,因此它可以设为书签并链接到这些地址。(这些约定是由全球互联网联盟(W3C)在上设定的)

转载于:https://www.cnblogs.com/KeSaga/p/5550263.html

你可能感兴趣的文章
【BZOJ】3142: [Hnoi2013]数列
查看>>
http初探
查看>>
elasticsearch的安装
查看>>
__next__()
查看>>
爬取:中国大学排名
查看>>
聊天室(C++客户端+Pyhton服务器)_1.框架搭设
查看>>
UpdatePanel 内控件 更新“外的”控件【转】
查看>>
mybatis中&gt;=和&lt;=的实现方式
查看>>
【Java自学】掷骰子游戏
查看>>
MySQL下查看用户和建立用户
查看>>
idea上 实现了Serializable接口,要自动生成serialVersionUID的方法
查看>>
error log
查看>>
Android自由行之走进zxing,轻松实现二维码扫描
查看>>
PowerDesigner显示Comment注释
查看>>
Hirbernate第三次试题分析
查看>>
文件及目录的权限详解
查看>>
setTimeout和setInterval的使用
查看>>
动态路由匹配
查看>>
.net 程序错误记录日志并发送邮件
查看>>
将Map转换为Java 对象
查看>>