zoukankan      html  css  js  c++  java
  • [翻译]ASP.NET MVC 3 开发的20个秘诀(十九)[20 Recipes for Programming MVC 3]:路由用户至特定的Controller或Action

    议题

    在今天投巨资转战各个搜索引擎霸主地位的战役中,像下面这个网站地址,想赢得比赛是相当困难的:

    http://www.example.com/books/details?id=4 

    使用Routes转换之后,地址看起来是这样的:

    http://www.example.com/20-recipes-for-mvc3

    这个URL地址,为用户和搜索引擎提供了更多的内容。

     

    解决方案

    使用RouteCollectionExtensions类的MapRoute功能来生成更友好的名称来显示内容,而不是使用数字标识。

     

    讨论

    路由信息可以在Web.configGlobal.asax.cs中设置。在Web.config文件中添加System.Web.Routing程序集引用,并在Global.asax.cs文件中创建一个默认的路由机制,去适配所有的控制器和动作。因此,当添加“BooksController”控制器之后,可以不使用扩展名,而通过“/Books”的URL,就像访问普通ASP.NET站点一样访问。

     

    下面的秘诀将会展示几种不同的设置路由的方式。第一种方式是允许使用书籍标题作为网站链接。例如,有本书名为“20 Recipes for Programming MVC 3”就可以使用如下网址直接访问:

    http://localhost/20 Recipes for Programming MVC 3

     

    而不是当前解决方案中更为复杂的URL,如:

    http://localhost/Books/Details?id=1

     

     

    要使用这个技术方案,首先要在MVC项目中打开Global.asax.cs。当网页第一次加载的时候,会在Application_Start()中调用RegisterRoutes()方法创建一个默认路由,下面的这个例子包含创建新的MapRoute的功能。修改RegisterRoutes方法如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using MvcApplication4.Models;
    using System.Data.Entity;
    using System.Globalization;
    using System.Threading;
    namespace MvcApplication4
    {
    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.MapRoute(
    "BookName", // Route name
    "{Keyword}", // URL with parameters
    new { controller = "Books", action = "Index",
    id = UrlParameter.Optional },
    new { Keyword = "\\w+" });

    routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}",
    // URL with parameters
    new { controller = "Home", action = "Index",
    id = UrlParameter.Optional }
    );
    }

    protected void Application_Start()
    {
    Database.SetInitializer<BookDBContext>(
    new BookInitializer());
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    }

    protected void Application_AcquireRequestState(
    object sender, EventArgs e)
    {
    if (HttpContext.Current.Session != null)
    {
    CultureInfo ci =
    (CultureInfo)this.Session["CurrentLanguage"];
    if (ci == null)
    {
    ci = new CultureInfo("en");
    this.Session["CurrentLanguage"] = ci;
    }
    Thread.CurrentThread.CurrentUICulture = ci;
    Thread.CurrentThread.CurrentCulture =
    CultureInfo.CreateSpecificCulture(ci.Name);
    }
    }
    }
    }

    在上面这个例子中,MapRoute方法接受了四个参数:

        1. 路由名称,当前是“BookName”;
        2. URL参数名称,在这里是“{Keyword}”,这个是变量名称,稍后会使用到;
        3. 这个参数要定义默认的控制器,动作以及附加参数;在这个例子中,默认控制器为“Books”,默认动作为“Index”;
        4. 这个是URL的约定参数(例如:变量);在这个例子中,将之前提到的变量“keyword”传递给BooksController中的Index动作。

     

    上述这个路由还可以结合之前做过的BooksController中的搜索的例子,如果只找到一个结果,则直接显示详细信息页面。这位用户提供了一种输入方式,在域名输入之后,用户输入了图书标题或关键字。如果只有一个返回结果,用户将看到这本书;否则,用户将看到与她们输入的关键字相关的搜索结果。

     

    在接下来的例子中,通过继承RouteBase类,将会创建更为复杂的路由功能,实现通过书的标题来定义网站子域名的功能。例如,http://mvc3book.localhost/将返回“20 Recipes for Programming MVC 3”的书籍详情页面。

     

    为了实现这个功能,必须修改“Book”模型类,添加新成员“ShortName”。通过创建继承RouteBase类的派生类来实现将书名做为子域名的参数来搜索书籍。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.ComponentModel.DataAnnotations;
    using MvcApplication4.Validations;
    namespace MvcApplication4.Models
    {
    public class Book
    {
    public int ID { get; set; }

    [Required]
    public string ShortName { get; set; }

    [Required]
    [Display(Name = "TitleDisplay", ResourceType =
    typeof(Resources.Resource1))]
    public string Title { get; set; }

    [Display(Name = "IsbnDisplay", ResourceType =
    typeof(Resources.Resource1))]
    [Required]
    [IsbnValidation]
    public string Isbn { get; set; }

    [Display(Name = "SummaryDisplay", ResourceType =
    typeof(Resources.Resource1))]
    [Required]
    public string Summary { get; set; }

    [Display(Name = "AuthorDisplay", ResourceType =
    typeof(Resources.Resource1))]
    [Required]
    public string Author { get; set; }

    [Display(Name = "ThumbnailDisplay", ResourceType =
    typeof(Resources.Resource1))]
    public string Thumbnail { get; set; }

    [Display(Name = "PriceDisplay", ResourceType =
    typeof(Resources.Resource1))]
    [Range(1, 100)]
    public double Price { get; set; }

    [Display(Name = "PublishedDisplay", ResourceType =
    typeof(Resources.Resource1))]
    [DataType(DataType.Date)]
    [Required]
    public DateTime Published { get; set; }
    }
    }

    现在需要创建一个新类来继承并实现新路由的逻辑。右键单击“Utils”文件夹,选择“添加”→“类”,将其命名为“BookDomainRoute.cs”。在下面的类中,将从HttpContextRequest.Headers对象中获取当前域名,使用“.”字符将域名分割为字符串数组。执行错误检查,以确保当前子域名不是“www”。然后获取子域名在图书的“ShrotName”字段中查找特定图书,如果找到该图书,则创建RouteData对象,然后添加Books控制器、Index动作以及该图书的ID,并返回RouteData对象。如果没有找到,则显示该页面。在下面的这个例子中很容易就可以将错误页面的例子改为“Books/Index”的关键字搜索页面(如上例所示)。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Routing;
    using System.Web.Mvc;
    using MvcApplication4.Models;
    namespace MvcApplication4.Utils
    {
    public class BookDomainRoute : RouteBase
    {
    private BookDBContext db = new BookDBContext();

    public override RouteData GetRouteData(
    HttpContextBase httpContext)
    {
    // Get the domain name
    var url = httpContext.Request.Url.Authority;

    // Split into array of parts
    var pieces = url.Split('.');

    // Ensure there is a subdomain and it's not www
    if (pieces.Length < 2 && pieces[0] != "www")
    {
    return null;
    }

    string ShortName = pieces[0];

    // Find the book by ShortName
    var books = from b in db.Books select b;

    books = books.Where(b =>
    b.ShortName.ToUpper().Contains(ShortName.ToUpper())
    );

    // Check to make sure a book was found
    if (books.Count() == 0)
    {
    return null;
    }

    // Get the first result
    Book book = books.First();

    // Set the route data
    RouteData routeData = new RouteData(this,
    new MvcRouteHandler());
    routeData.Values.Add("controller", "Books");
    routeData.Values.Add("action", "Details");
    routeData.Values.Add("id", book.ID);
    return routeData;
    }

    public override VirtualPathData GetVirtualPath(
    RequestContext requestContext,
    RouteValueDictionary values)
    {
    return null;
    }
    }

    }

    最后,修改Global.asax.cs文件,将刚才创建的新路由添加到文件中。使用using语句将Utils目录的引用添加到命名空间引用中,才能找到新创建的路由类。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using MvcApplication4.Models;
    using System.Data.Entity;
    using System.Globalization;
    using System.Threading;
    using MvcApplication4.Utils;
    namespace MvcApplication4
    {
    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 BookDomainRoute());
    routes.MapRoute(
    "BookName", // Route name
    "{Keyword}", // URL with parameters
    new { controller = "Books", action = "Index",
    id = UrlParameter.Optional },
    new { Keyword = "\\w+" });

    routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}",
    // URL with parameters
    new { controller = "Home", action = "Index",
    id = UrlParameter.Optional }
    );
    }
    protected void Application_Start()
    {
    Database.SetInitializer<BookDBContext>(
    new BookInitializer());
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    }
    protected void Application_AcquireRequestState(
    object sender, EventArgs e)
    {
    if (HttpContext.Current.Session != null)
    {
    CultureInfo ci =
    (CultureInfo)this.Session["CurrentLanguage"];
    if (ci == null)
    {
    ci = new CultureInfo("en");
    this.Session["CurrentLanguage"] = ci;
    }
    Thread.CurrentThread.CurrentUICulture = ci;
    Thread.CurrentThread.CurrentCulture =
    CultureInfo.CreateSpecificCulture(ci.Name);
    }
    }
    }
    }

    这个例子是一个非常好的设计体系,使用这种设计方案可以很容易的创建其他的路由,例如,显示某用户的个人资料页或修改多语言版本的秘诀,允许使用路由的方式通过URL切换不同语言版本,例如:en.example.comen.example.com

     

    参考

    RouteCollectionExtensions RouteData 原书地址 书籍源代码

  • 相关阅读:
    linux设置tomcat开机自启动
    搭建本地的git仓库
    Linux安装配置JDK
    关于HTTP GET请求的url中文参数编码
    [dataTables.js error] Uncaught TypeError: myTable.row is not a function
    DataTables warning : Requested unknown parameter '5' from the data source for row 0
    取消mod_sofia的呼叫鉴权
    TCP服务器不回复SYN的问题
    volley7--NetworkDispatcher从网络中获取数据
    volley4--RequestQueue
  • 原文地址:https://www.cnblogs.com/o2ds/p/2313529.html
Copyright © 2011-2022 走看看