# 路由
[TOC=2,3]
當用戶訪問一個 URL 時,最終執行哪個模塊下哪個控制器的哪個操作,這是由路由來解析后決定的。
ThinkJS 提供了一套靈活的路由機制,除了默認的解析外,還可以支持多種形式的自定義路由,讓 URL 更加簡單友好。
## URL 解析為 pathname
當用戶訪問服務時,服務端首先拿到的是一個完整的 URL,如:訪問本頁面,得到的 URL 為`http://www.thinkjs.org/zh-CN/doc/2.0/route.html`。
將 URL 進行解析得到的 pathname 為?`/zh-CN/doc/2.0/route.html`。
## pathname 過濾
有時候為了搜索引擎優化或者一些其他的原因, URL 上會多加一些東西。比如:當前頁面是一個動態頁面,但 URL 最后加了?`.html`,這樣對搜索引擎更加友好。但這些在后續的路由解析中是無用的,需要去除。
ThinkJS 里提供了下面的配置可以去除?`pathname`?的前綴和后綴內容:
~~~
export default {
pathname_prefix: "",
pathname_suffix: ".html",
}
~~~
上面配置可以在?`src/common/config/config.js`?中進行修改。
pathname 過濾時會自動去除左右的?`/`,該邏輯不受上面的配置影響。對 pathname 進行過濾后,拿到干凈的 pathname 為?`zh-CN/doc/2.0/route`。
> 注:?如果訪問的 URL 是?`http://www.thinkjs.org/`,那么最后拿到干凈的 pathname 則為空字符串。
## 子域名部署
當項目比較復雜時,可能希望將不同的功能部署在不同的域名下,但代碼還是在一個項目下。如:域名`admin.example.com`?部署后臺管理的功能,希望映射到?`admin`?模塊下。
ThinkJS 提供了如下的配置可以進行子域名部署,該配置可以在?`config/config.js`?里設置:
~~~
export default {
subdomain: {
admin: "admin", //表示將 admin.example.com 映射到 admin 模塊下
...
}
}
~~~
假如過濾后的 pathname 為?`group/detail`,命中了 admin.example.com 這個子域名后,pathname 變為`admin/group/detail`。
## 路由識別
### 路由解析
路由識別默認根據?`模塊/控制器/操作/參數1/參數1值/參數2/參數2值`?來識別過濾后的 pathname,如:pathname 為?`admin/group/detail`,那么識別后的結果為:
* module 為?`admin`
* controller 為?`group`
* action 為?`detail`,對應的方法名為?`detailAction`
如果項目里并沒有?`admin`?這個模塊或者這個模塊被禁用了,那么識別后的結果為:
* module 為默認模塊?`home`
* controller 為?`admin`
* action 為?`group`,對應的方法名為?`groupAction`
* 參數為?`{detail: ''}`
### 大小寫轉化
路由識別后,`module`、`controller`?和?`Action`?值都會自動轉為小寫。如果 Action 值里有?`_`,會作一些轉化,如:假設識別后的 Controller 值為?`index`,Action 值為?`user_add`,那么對應的 Action 方法名為`userAddAction`,但模版名還是?`index_user_add.html`。
## 路由默認值
當解析 pathname 沒有對應的值時,此時便使用對應的默認值。其中 module 默認值為?`home`,controller 默認值為?`index`,action 默認值為?`index`。
這些值可以通過下面的配置進行修改,配置文件?`src/common/config/config.js`:
~~~
export default {
default_module: "home",
default_controller: "index",
default_action: "index",
}
~~~
## 自定義路由
默認的路由雖然看起來清晰明了,解析起來也很簡單,但看起來不夠簡潔。
有時候需要更加簡潔的路由,這時候就需要使用自定義路由解析了。如:文章的詳細頁面,默認路由可能是:`article/detail/id/10`,但我們想要的 URL 是?`article/10`?這種更簡潔的方式。
#### 開啟配置
開啟自定義路由,需要在?`src/common/config/config.js`?開啟如下的配置:
~~~
export default {
route_on: true
}
~~~
#### 路由規則
開啟自定義路由后,就可以通過路由規則來定義具體的路由了,路由配置文件為:`src/common/config/route.js`,格式如下:
~~~
export default [
["規則1", "需要識別成的pathname"],
["規則2", {
get: "get請求下需要識別成的pathname",
post: "post請求下需要識別成的pathname"
}]
];
~~~
> 注:?自定義路由每一條規則都是一個數組。(至于為什么不用對象,是因為正則路由下,正則不能作為對象的 key 直接使用)
#### 識別方式
自定義路由的匹配規則為:從前向后逐一匹配,如果命中到了該項規則,則不在向后匹配。
* * *
ThinkJS 支持 3 種類型的自定義路由,即:正則路由,規則路由和靜態路由。
### 正則路由
正則路由是采用正則表示式來定義路由的一種方式,依靠強大的正則表達式,能夠定義非常靈活的路由規則。
~~~
export default [
[/^article\/(\d+)$/, "home/article/detail?id=:1"]
];
~~~
上面的正則會匹配類似?`article/10`?這樣的 pathname,識別后新的 pathname 為?`home/article/detail`,并且將捕獲到的值賦值給參數 id ,這樣在控制器里就可以通過?`this.get`?方法 來獲取該值。
~~~
export default class extends think.controller.base {
detailAction(){
let id = this.get("id");
}
}
~~~
如果正則里含有多個子分組,那么可以通過?`:1`,`:2`,`:3`?來獲取對應的值。
~~~
export default [
[/^article\/(\d+)$/, {
get: "home/article/detail?id=:1",
delete: "home/article/delete?id=:1",
post: "home/article/save?id=:1"
}]
];
~~~
### 規則路由
規則路由是一種字符串匹配方式,但支持含有一些動態的值。如:
~~~
export default [
["group/:year/:month", "home/group/list"]
]
~~~
假如訪問的 URL 為?`http://www.example.com/group/2015/10`,那么會命中該項規則,得到的 pathname 為`home/group/list`,同時會添加 2 個參數?`year`?和?`month`,這2個參數可以在控制器里通過?`this.get`?方法來獲取。
~~~
export default class extends think.controller.base {
listAction(){
let year = this.get("year");
let month = this.get("month");
}
}
~~~
### 靜態路由
靜態路由是一種純字符串的完全匹配方式,寫法和識別都很簡單,功能也相對要弱很多。
~~~
export default [
["list", "home/article/list"]
]
~~~
假如訪問的 URL 為?`http://www.example.com/list`,那么替換后的 pathname 為?`home/article/list`。
### 優化路由性能
上面已經說到,自定義路由是個數組,數組每一項是個具體的路由規則,匹配時是從前向后逐一進行匹配。如果這個路由表比較大的話,可能會有性能問題。
為了避免有性能問題,ThinkJS 提供了一種更加高效的自定義路由方式,按模塊來配置路由。使用這種方式,路由配置格式跟上面稍微有所不同。
#### common/config/route.js
使用這種方式后,通用模塊里的路由配置不再配置具體的路由規則,而是配置哪些規則命中到哪個模塊。如:
~~~
export default {
admin: {
reg: /^admin/ //命中 admin 模塊的正則
},
home: { //默認走 home 模塊
}
}
~~~
#### admin/config/route.js
admin 模塊配置 admin 下的具體路由規則。
~~~
export default [
[/^admin\/(?!api).*$/, "admin/index"],
[/^admin\/api\/(\w+?)(?:\/([\d,]*))?$/, "admin/:1?id=:2&resource=:1"],
];
~~~
* * *
假設訪問的 URL 為?`http://www.example.com/admin/api`,那么解析后的 pathname 為?`admin/api`,匹配`common`?里的規則時會命中?`admin`?模塊,然后再對?`admin`?模塊下的路由規則進行逐一匹配。通過這種方式后就可以大大減少路由規則匹配的數量,提供匹配效率。
- 快速入門
- 介紹
- 創建項目
- 項目結構
- 代碼規范
- 升級指南
- 進階應用
- 模塊
- 控制器
- 視圖
- 配置
- 路由
- 模型
- 介紹
- 事務
- 關聯模型
- Mysql
- MongoDB
- SQLite
- Adapter
- 介紹
- Cache
- Session
- WebSocket
- Template
- 擴展功能
- thinkjs 命令
- 靜態資源訪問
- Middleware
- Service
- Cookie
- 錯誤處理
- 錯誤信息
- 數據校驗
- 國際化
- 路徑常量
- REST API
- 定時任務
- 線上部署
- 推薦模塊
- API
- think
- think.base
- think.http.base
- http
- controller
- rest controller
- model
- model.mongo
- middleware