词条统计
浏览次数:6869 次
编辑次数:8次 历史版本
最近更新:2013/6/20
创建者:掷鸡蛋者

客户端向服务器发送请求,主要是通过 url 链接的形式,是 url 告诉了服务器,应该返回什么样的信息。

路由(route)系统的目的,主要也就是将 url 解析成特定的数据,比如 url 中的 controller 是什么,action 是什么,id 又是什么,翻页是第几页等等。

在 wojilu mvc 中有一个 RouteTool,就是专门用来解析 url 的。
    Route r = RouteTool.Recognize( context.ctx );

通过 Recognize 方法,就得到了结果,也就是一个 Route 对象,各属性的值是:

ctx.route.id当前ID(整数)
ctx.route.controller当前控制器(字符串)
ctx.route.action当前控制器的方法(字符串)
ctx.route.owner被访问对象(字符串)
ctx.route.ownerType被访问对象的类型(site/group/user等)(字符串)
ctx.route.appId当前应用程序的appId(有时候可以为0)(整数)
ctx.route.page当前页面(在翻页的时候出现)(整数)
ctx.route.query当前url中的查询字符串(QueryString)(字符串)


wojilu mvc 在解析并获取到这些值之后,会传递给 MvcContext,然后框架根据 controller 和 action 的字符串,创建相应的 controller 对象。这些过程,都是框架自动处理的。所以,开发者一般不需要用到 ctx.route.controller 等的字符串的值。

下面举个例子,比如 Article/2/Edit.aspx 这个 url ,在被解析之后,其实对应着 ArticleController 的 Edit(int id) 方法;
而 Article/2.aspx 这个 url,在被解析之后,则对应着  ArticleController 的 Show(int id) 方法,默认 Show 方法不在 url 中显示。

基本上,开发者不用操心链接的生成和路由的解析这个事情。只有在你需要自定义路由解析规则的时候,才需要理解下面的内容。

一、常规路由

路由的规则是由配置文件 /framework/config/route.config 定义的:



我们看第三行,这条规则把 url 分成两部分,controller和id部分,如果一个 url 是由两部分组成,并且第二部分符合后面的要求(requirements)是整数,那么这个 url 的第一部分就是 controller,第二部分就是 id,比如 Article/3.aspx 的控制器就是 ArticleController(Controller后缀会自动补上),ID就是 3。

下面几行路由规则的解析原理依次类推。

那么,第一条路由(第一行那个)是什么意思?它表示,如果请求的 url 是 default.aspx ,则默认(default)使用 MainController;但它没有指定默认的action。在没有 id 的情况下,默认的 action 是 Index;如果有 id ,则默认的 action 是 Show。 

二、自定义路由

有些网址比较长,比如 www.wojilu.com 的论坛网址实际是: http://www.wojilu.com/Forum1/Forum/Index 在域名后面的路径为 /Forum1/Forum/Index ,不容易记忆,这时候,自定义路由就派上了用场。请在 route.config 中增加一行:
    bbs;default:{ownertype=site,owner=site}

你可以和原先 route.config 中的第一行比较一下,其实是差不多的。它的意思是,bbs.aspx 将被解析到 site 对象的默认 controller 的默认 action 上,但是奇怪的是,没有定义默认的 controller?这是“我记录网站综合系统”在路由基础上,额外增加的一个功能,就是默认controller根据数据库中的菜单设置决定。这部分你可以进一步参考“我记录网站综合系统”。

我们甚至可以将首页指向特定的用户,比如某用户名的友好url是 zhangsan,那么如果你第一行修改成这样:
    default;default:{owner=zhangsan,ownertype=user}

那么,用户访问网站首页,就会直接看到 zhangsna 这个用户的空间首页。顺便说一下,这意味着你完全可以把 “我记录网站综合系统” 这个多用户系统当做个人独立博客使用,比起那些单用户博客系统来说,要方便、强大得很多。这就像你把 windows 2003 当 windows xp 用,把wondows 2008当 windows 7 用一个道理。另外,这样使用还有个好处,就是一旦你的独立博客有了一定知名度,你完全可以简单修改一下路由,就把它转换成支持“论坛/SNS”等在内的多用户系统。


三、查询参数


我们先看一下传统的方式。在传统的web开发中,一个页面向另外一个页面传递参数,是通过queryString的方式进行的,也就是在网址后面附上一个问号和几个参数列表,比如:

/product.aspx?factory=联想&category=pc

然后在服务端通过 Request.QueryString( "factory" ) 和 Request.QueryString( "category" ) 依次获得 factory 和 category 的值。


在wojilu框架中,这种方式仍然支持。但对 Request.QueryString 进行了简化,用 ctx.Get("") 代替,效果是一样的。也就是说 ctx.Get("factory") 和 Request.QueryString( "factory" ) 的值完全相同。


但是这种带问号?的参数传递方式有一些缺点,主要是SEO不够友好。传统的解决方法是使用url重写(rewrite)。大概思路这样:
1)设计一个对SEO友好的网址,比如 product/联想/pc.aspx
2)设计一个重写的正则匹配表,此处省略;
3)添加代码,在客户端请求到达服务器之后、正式解析之前,将 product/联想/pc.aspx 这样的网址解析成传统的 /product.aspx?factory=联想&category=pc 形式。


在 wojilu 框架中,不用重写(rewrite)技术,直接使用自定义路由功能,即可达到类似效果。


1)在路由中增加如下规则项:

search/{query};default:{controller=_.Main,action=Search}

product/{factory}/{category};default:{controller=_.Product,action=List}

book/{author};default:{controller=_.Book,action=List}


2)如果是 product/苹果/电脑.aspx 类似的网址,服务端可以通过 ctx.route.getItem( "factory" ) 来获得。以下是测试代码,供参考:

result = RouteTool.RecognizePath( "search/新闻" );

Assert.AreEqual( result.controller, "Main" );

Assert.AreEqual( result.action, "Search" );

Assert.AreEqual( result.query, "新闻" );


result = RouteTool.RecognizePath( "product/苹果/电脑" );

Assert.AreEqual( result.controller, "Product" );

Assert.AreEqual( result.action, "List" );

Assert.AreEqual( result.getItem( "factory" ), "苹果" );

Assert.AreEqual( result.getItem( "category" ), "电脑" );


result = RouteTool.RecognizePath( "book/金庸" );

Assert.AreEqual( result.controller, "Book" );

Assert.AreEqual( result.action, "List" );

Assert.AreEqual( result.getItem( "author" ), "金庸" );


3)如何生成 product/苹果/电脑.aspx   类似的链接?默认的 Link.To 或者 controller 自带的to方法,并不支持字符串参数,所以你需要自己写一个帮助方法来生成链接,具体方法就是拼接字符串,比如:
public String getProductLink( String factory, String category ) {
return "/product/" + factory + "/" + category + MvcConfig.Instance.UrlExt;

}


【常见问题】

比如自定义路由——

article/{typeid}/{id};default:{controller=Article,action=List}

却发生错误——

url=http://localhost:53091/article/12/1 

ex.Message=控制器不存在: article.Article 


请将自定义路由的controller前面加上“下划线和点号”,如下

article/{typeid}/{id};default:{controller=_.Article,action=List}

“下划线和点号”表示前面没有namespace,否则article会被加上。


补充:关于双向路由

熟悉 rails 的用户,会关心双向路由的问题。我的答案是,目前 wojilu mvc 不支持双向路由。原因是 wojilu mvc 要支持namespace,要支持不带查询形式(即?page=99这种url)的翻页链接,所以目前没有使用双向路由。


但使用上面的自定义路由功能,一样能得到类似的效果。另外,wojilu mvc 在实践中发现,虽然不支持双向路由,但一样能工作得非常良好,“我记录网站综合系统”就是一个活生生的项目例子。还有,因为 wojilu mvc 支持 namespace,所以你完全可以通过controller灵活的目录划分方式,来获得具有 RESTfull 风格的、语义清晰的url。