http://www.cnblogs.com/HuiTai/archive/2012/07/24/2597875.html
接着前面继续学习分享我们的路由。
现在我们把Global.asax文件里的RegisterRoutes方法还原至原来的样式,具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Routing.Infrastructure; namespace Routing { // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明, // 请访问 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { //整理RegisterRoutes routes.MapRoute("MyRoute", "{controller}/{action}/id", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); } } }
生成的URL在视图
生成传出URL最简单的方法是在视图里调用内的Html.ActionLink方法。具体代码如下:
@Html.ActionLink("About this application", "About")
ActionLink的方法的参数为文本链接和操作方法的名称,应针对链接。ActionLink的方法生成的HTML的基础上,在当前的路由架构。上面运行后的HTML代码表示如下:
<a href="/Home/About">About this application</a>
但假设我们改变路由模式通过添加一个新的路由,像下面:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("NewRoute", "App/Do{Action}", new { controller = "Home" }); //整理RegisterRoutes routes.MapRoute("MyRoute", "{controller}/{action}/id", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); }
然后我们得到了下面的HTML从ActionLinkhelper方法当我们呈现页面:
<a href="/App/DoAbout">About this application</a>
在这种方式生成的链接如何解决维修问题。我们能够改变我们的路由模式,即将生成自动映射这一变化。
指向其他控制器(Controller)
默认的版本的ActionLink方法,假定您想要一个动作方法为目标在同一个控制器已经造成视图呈现出来。创建一个新生成的URL,目标是不同的控制器您可以使用一个不同的重载,它允许您指定控制器的名字,具体代码如下:
@Html.ActionLink("About this application", "About", "MyController")
当你运行器项目,你会看到他的连接会是这样的,具体代码如下:
<a href="/MyController/About">About this application</a>
传递额外的值
我们可以通过段使用匿名类型的变量的值,用属性代表段,具体如下:
@Html.ActionLink("About this application", "About", new { id = "MyID" })
我们已经提供了一个值为一段变量称为id。当我跑起项目,我们可以看到这样的连接,具体如下:
<a href="/Home/About/MyID">About this application</a>
我们提供的参数值已经添加作为URL的细分部分,以匹配的URL模式到我们的应用程序的路径。
当我们提供的不符合部分变量的属性值,值作为查询字符串附加到传出的URL,具体代码如下:
@Html.ActionLink("About this application", "About", new { id = "MyID", myVariable = "MyValue"})
他生成的HTML代码如下:
<a href="/Home/About/MyID?myVariable=MyValue">About this application</a>
如果我们提供一个变量值,正好符合我们在路由指定的默认值,然后路由系统省略了即将生成输出的URL变量,具体代码如下:
@Html.ActionLink("About this application", "Index", "Home")
我们作为参数传递的值的操作方法和控制器的匹配默认,运行后生成页面的HTML代码如下:
<a href="/">About this application</a>
指定的HTML属性
生成一个完整的HTML锚(<A>)的元素。我们可以通过提供一个设置该元素的属性匿名类型的属性对应到我们所需要的属性。下面一个示范设置一个id属性和分配的HTML元素的CSS类。
(生成一个锚元素和属性).具体代码如下:
@Html.ActionLink("About this application", "Index", "Home", null, new {id = "myAnchorID", @class = "myCSSClass"})
我们已经创建了一个新的匿名类型的id和class属性,并通过它作为一个ActionLink的方法参数。我们通过额外的变量值部分为null,表明我们没有提供任何值,运行后的HTML代码如下:
<a class="myCSSClass"href="/" id="myAnchorID">About this application</a>
生成一个完全合格的URL
到目前为止,我们已经产生含有相对URL,但我们也可以使用ActionLinkhelper方法来生成一个合格的URL,具体代码如下:
@Html.ActionLink("About this application", "Index", "Home", "https", "myserver.mydomain.com", " myFragmentName", new { id = "MyId"}, new { id = "myAnchorID", @class = "myCSSClass"})
这是ActionLink的载入大多数参数,它提供的值,目标服务器的名称(myserver.mydomain.com)和URL片段(myFragmentName),以及你以前看到的所有其他选项。当呈显在视图,运行之后的HTML如下:
<a class="myCSSClass" href="https://myserver.mydomain.com/Home/Index/MyId#myFragmentName" id="myAnchorID">About this application</a>
我们建议尽可能使用相对URL。完全合格的URL创建依赖关系的方式呈现给用户,您的应用程序基础设施。我们已经看到了许多大应用程序依赖不协调的变化打破了网络的绝对URL基础设施或域名政策,这也是我们无法管控的。
生成URL(非连接)
Html.ActionLink辅助方法生成完整的HTML<a>元素,这是正是我们想要的。然而,有的时候,我们只需要一个URL,这可能是因为我们要显示的URL,建立一个链接的HTML手动显示的URL值,或包含作为一个数据元素所呈现的HTML页面的网址。
在这种情况下,我们可以使用生成的URL,而不是Url.Action方法周围的HTML,具体代码如下:
... My URL is: @Url.Action("Index", "Home", new { id = "MyId" }) ...
Url.Action方法的Html.ActionLink方法相同的方式工作,但它生成唯一的URL。运行后的HTML代码如下:
My URL is: /Home/Index/MyId
路由数据生成的链接和URL
前面提到,路由系统不指定任何特殊的意义控制器行动部分的变量,当它被处理的URL。附加到MVC框架,允许路由系统可以更广泛地与其他类型的ASP.NET应用程序使用。有时它是用于对待控制器和行动,就像任何其他变量。并产生链接或URL提供一个 Name/Value 对的集合。我们可以通过使用辅助方法不是具体的mvc特性。
下面看 生成一个链接使用匿名类型,具体代码如下:
@Html.RouteLink("Routed Link", new { controller = "Home", action = "About", id="MyID"})
有没有参数,为RouteLink方法来表达控制器和行动值。我们必须包括他们作为匿名类型的属性。运行后的HTML代码如下:
<a href="/Home/About/MyID">Routed Link</a>
我们也可以使用 Url.RouteUrl辅助方法来生成网址,具体代码如下:
@Url.RouteUrl(new { controller = "Home", action = "About", id = "MyID" })
这些方法都很少需要,因为我们一般都知道,要指定控制器和行动明确的值。但它是很好的了解,这些方法都存在,当你需要他们轻松很多,但是很少可能。
在操作(Action)方法生成传出URL
大多数情况下,我们需要生成传出网址,但有时,当我们想做些什么类似的内部操作方法。如果我们只需要生成一个URL,我们可以使用相同的辅助方法我们在视图中使用,具体代码如下:
public ViewResult MyActionMethod() { string myActionUrl = Url.Action("Index", new { id = "MyID" }); string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "Index" }); ... }
一个更常见的需求是重定向客户端浏览器到另一个URL。我们可以通过调用RedirectToAction方法,例如下面代码:
public ActionResult MyActionMethod() { return this.RedirectToAction("Index"); }
RedirectToAction方法的结果是RedirectToRouteResult,指示的MVC框架发出重定向的URL将调用指定的动作指令。还有一些通常RedirectToAction方法的重载版本,指定控制器和值在生成的URL段的变量。如果你想发送重定向使用从对象的属性生成一个URL,你可以使用RedirectToRoute方法,(重定向到一个URL生成一个匿名类型的属性),具体代码如下:
public ActionResult MyOtherActionMethod() { return RedirectToRoute(new { controller = "Home", action = "Index", id = "MyID" }); }
这种方法也返回一个RedirectToRouteResult对象,并调用具有完全相同的效果RedirectToAction方法.
从一个特定的路由生成一个URL
例如,我们定义下面的路由:
- routes.MapRoute("MyRoute", "{controller}/{action}");
- routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" });
我们作为第一个参数传递的路由,我们创建指定名称的MapRoute方法这种情况下,MyRoute和MyOtherRoute。
命名你的路由有两个原因:
- 作为一种提醒路由的目的
- 你可以选择一个特定的路由被用来生成一个传出的URL
我们已安排的路由,使最不特定的列表中首先出现。这意味着,如果我们要生成一个链接使用这样的ActionLink的方法,具体代码如下:
@Html.ActionLink("Click me", "About");
你可以通过默认路由匹配的行为使用Html.RouteLink方法,这让你指定你要使用哪条路线,具体代码如下:
@Html.RouteLink("Click me", "MyOtherRoute", new { action = "About" });
★定制路由体制★
我们已经看到如何灵活和可配置的路由系统,但如果它不符合我们的的要求,那么我们可以自定义的行为。
创建自定义RouteBase实现
如果你不喜欢标准Route对象匹配的URL的方式,或想实现的东西不寻常的,可以替代类从RouteBase派生。这使你可以匹配URL,参数提取,并传出的URL是如何传出的。
从RouteBase得出一个类,您需要实现两个方法:
- GetRouteData(HttpContextBase httpContext):这是该机制匹配入站URL。框架调用此方法对每个反过来RouteTable.Routesentry,直到其中一个返回一个非的NullValue。
- GetVirtualPath(RequestContext requestContext, RouteValueDictionary values):这是路由工作匹配出站URL生成的机制。 框架调用方法上每个反过来RouteTable.Routesentry,直到之一他们返回一个非的NullValue。
为了证明这种定制,我们要创建一个RouteBase类将处理传统的URL请求。试想一下,我们现有的应用程序迁移到MVC框架,但有些MVC URL或硬编码成剧本。我们仍然希望支持这些旧的网址。我们可以处理这种使用常规的路由机制。开始,我们需要创建一个控制器,我们将在收到我们的传统要求。我们需要通知给我们控制器LegacyController(这里从新创建一个项目"Routing_Project"),具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Routing_Project.Controllers { public class LegacyController : Controller { public ActionResult GetLegacyURL(string legacyURL) { return this.View((object)legacyURL); } } }
在这个简单的控制器的GetLegacyURL操作方法需要的参数,并通过它作为一个查看视图的模型。如果我们真的实现此控制器,我们将使用此方法检索请求的文件,因为我们只是要在一个视图中显示的URL。对应的视图GetLegacyURL.cshtml代码如下:
@model string @{ ViewBag.Title = "GetLegacyURL"; this.Layout = null; } <h2>GetLegacyURL</h2> The URL Requested Was:@Model
这是很简单的。我们只要证明自定义的路由行为,所以我们不要花费任何时间创建复杂的行动和视图。现在,我们已经达到目的,我们可以创建派生RouteBase。
路由传入的URL
我们已创建LegacyRoute类,这是我们放在顶层文件夹("Infrastructure").具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace Routing_Project.Infrastructure { public class LegacyRoute : RouteBase { private string[] urls; //构造函数 public LegacyRoute(params string[] targetUrls) { this.urls = targetUrls; } public override RouteData GetRouteData(HttpContextBase httpContext) { RouteData result = null; //获取应用程序的虚拟目录 string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath; if (this.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; } } }
这个类的构造函数接受一个字符串数组,代表网址将支持路由类。我们会指定这些当我们以后注册的路由。GetRouteData方法,这就是路由系统调用来看看我们是否能够处理传入URL。
如果我们不能处理的要求,那么我们可以直接返回null,路由机制将移动到列表中的下一个路由并重复这个过程。如果我们能够处理的请求,我们需要返回一个RouteData类包含控制器和操作变量的值的实例,别的我们要传递的操作方法。接下来我们需要注册一条路由,具体的代码在Global.asax添加如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Routing_Project.Infrastructure; namespace Routing_Project { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //注册一条路由 routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library")); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); } } }
我们创建一个新的类的实例传递的URL希望它来自路由。然后,我们添加RouteCollection使用Add方法。现在,当我们要求旧的URL在我们自定义的类,路由请求和导演对我们的控制器如下图1所示。
图1.
生成外向的URL
为了支持即将向外生成的URL,我们需要实现的GetVirtualPath方法。如果我们无法处理请求,我们让路由系统知道返回null。否则,我们返回一个的VirtualPathData类的实例。具体修改如下代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace Routing_Project.Infrastructure { public class LegacyRoute : RouteBase { private string[] urls; //构造函数 public LegacyRoute(params string[] targetUrls) { this.urls = targetUrls; } public override RouteData GetRouteData(HttpContextBase httpContext) { RouteData result = null; //获取应用程序的虚拟目录 string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath; if (this.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) { VirtualPathData result = null; if (values.ContainsKey("legacyURL")&& this.urls.Contains((string)values["legacyURL"],StringComparer.OrdinalIgnoreCase)) { result = new VirtualPathData(this, new UrlHelper(requestContext).Content((string)values["legacyURL"]).Substring(1)); } return result; } } }
我们已经通过细分变量和其他细节在使用匿名类型,但幕后,路由系统已RouteValueDictionary对象转换成这些。因此我们添加这么一个视图代码如下:
@Html.ActionLink("Click me", "GetLegacyURL", new { legacyURL = "~/articles/Windows_3.1_Overview.html" })
创建与legacyURL属性的匿名类型转换成RouteValueDictionary类包含同名的关键。在这个例子中,我们决定,我们可以处理一个请求外出URL,如果有名为legacyURL一个关键,如果它的值是一个URL,被传递到构造。我们可以更具体和检查控制器和行动值,在简单的应用中这些应该可以满足的!
创建一个自定义路由处理程序
我们依靠MvcRouteHandler在我们的路由,因为它连接到MVC的路由机制。由于我们的重点是MVC框架,这是我们想要的几乎所有的时间关注的。既然如此,路由机制,让我们定义我们自己的路由处理实施IRouteHandler接口。具体代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Routing; namespace Routing_Project.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实现控制器被发现,这些接口,调用的操作方法,观点呈现,结果
被写入响应。我们的实现是简单一点。它只是写的字打招呼客户端(不含有这个词的一个HTML文件,但只是文本)。OK,我们依然还是需要注册路由,具体如下:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //注册一条路由 routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library")); //使用一个自定义路由处理器的路线 routes.Add(new Route("SayHello", new CustomRouteHandler())); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); }
运行项目,我们访问“ URL /SayHello”,结果如下图2
图2.
处理领域
MVC框架支持组织一个web应用程序到的区域,每个区域代表应用程序的功能性组比如账单、客户支持,等等,这在一个大的项目是非常有用的,那里有一套单一的文件夹,所有的控制器,视图和模型可以变得难以管理。每个MVC区域是有自己的文件夹结构,允许您分开管理。这使得它更显而易见哪个项目元素相互关联应用程序的功能区域,这有助于多个开发人员同事处理项目而没有彼此胡想不干扰。区域是支持主要通过路由机制。
我们从新新建一个MVCweb应用程序("MVCArea"),创建好项目,我们直接演示怎么给项目添加一个区域进来具体如下图3.-4.
图3.图4.当我们创建好我们的区域之后项目的结构会变成如下图5所示。
图5.OK这里你可以看一套类似与MVC文件机制东东出来,这个就是我们创建的区域关于这个东西后面慢慢来学习他,我们现在只关心他那个自动创建AdminAreaRegistration.cs的文件里面是怎么一回事,不看内容就凭着这个名字猜一下,大概就路由扯上关系了。OK,打开看看,AdminAreaRegistration.cs的代码如下:
using System.Web.Mvc; namespace MvcArea.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 } ); } } }
OK,可以看出上面代码我加粗标识的RegisterArea方法十分有意思,在这个区域里注册一个路由的URL模式" Admin/{controller}/{action}/{id}",当然我们可以在这里定义其他的URL模式,但是你要知道你这这里定义的话只在该区域里有效,也就是你额外定义的路由机制的持有权在该区域内。
我们不需要采取任何行动来确保这个注册方法被调用。因为Global.asax文件里的Application_Start方法在我们创建区域的时候为自动为我们注册进去,那么我们来打开Global.asax文件里的Application_Start方法看看,具体代码如下:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); }
可以看到是OK的,接下我们在我们创建的Admin区域里添加一个控制器("Controller")和方法("Action")和一些视图("View")来看看效果,首先添加一个控制器("Controller"),如下图6.
图6.创建HomeController,具体的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcArea.Areas.Admin.Controllers { public class HomeController : Controller { // // GET: /Admin/Home/ public ActionResult Index() { return this.View(); } } }
OK,我们在创建一个视图,如下图7.
图7。创建好Index.cshtml,页面的代码如下:
@{ ViewBag.Title = "Index"; } <h2>Admin Area Index</h2>
OK,我们试着运行一下我们的Web项目,并且访问一下他的路由" /Admin/Home/Index",结果如下图8.
图8.
貌似我们撒了一个谎言,我们试着访问一下我们web项目的根路径看看什么效果,运行如下图9.
图9.
OK,MVC路由机制一下找到了2个"HomeController"这下不知道去那个,就出错了!
当一个地区注册,任何路由,我们定义仅限于名称空间关联该区域。这就是我们为什么请求" /Admin/Home/Index "的时候。路由机制找到"HomeController"的命名空间MvcArea.Areas.Admin.Controllers。但是 Global.asax文件的机制可不是这个样子的,来看看我们默认的 Global.asax文件是怎么搞的,具体代码如下:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); }
名为default的路由转换传入的URL从浏览器到主控制器HomeController行动上Index方法。在这一点上,我们得到一个错误,因为有没有命名空间的限制这条路线和MVC框架,可以看到两个的HomeController类。所以我们就可以看到上面悲剧发生,我们对Global.asax文件做简单的处理,具体操作如下:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults new[] { "MvcArea.Controllers" } ); }
这种变化,确保在控制器的主要项目,给予优先解决的请求。
MVC框架中检测到当前的请求与特定的区域, 然后出站的URL生成将会找到一个匹配只有在路线定义为该领域。比如在我们的Index.cshtml修改如下代码所示:
@{ ViewBag.Title = "Index"; } <h2>Admin Area Index</h2> @Html.ActionLink("Cilck me", "About")
运行项目,可以看到如下图10的生成的连接.
图10.
当然也可以修改如下代码所示(创建一个链接到一个动作在不同的区域,或没有区域内,您必须创建一个变量调用地区并使用它来指定区域的名字你想要的):
@{ ViewBag.Title = "Index"; } <h2>Admin Area Index</h2> @Html.ActionLink("Cilck me", "About") <br /> @Html.ActionLink("Click me to go to another area", "Index", new { area = "Support" })