概要
Rocket 提供最基礎的架構來構建rust服務端應用:剩下的取決于你自己。簡而言之,Rocket提供路由,請求前置處理和相應后置處理。至于,請求前置如何處理,相應后置如何處理,請求如何處理,都是你的業務代碼決定的。
生命周期
Rocket的主要任務時監聽新來的web請求,分發這些請求到業務代碼,并且返回響應到客戶端。我們把從請求到響應的過程叫做生命周期。我們把生命周期總結為一下幾個步驟:
- 路由
Rocket 會把新來的HTTP請求解析為你的代碼可以操作的rust結構。并且通過匹配你程序中聲明的路由屬性,來檢測,應該調用那個一個處理器。 - 驗證
Rocket在匹配了路由以后就會對請求的數據做類型及有效性的驗證。如果驗證失敗,則Rocket會把請求轉到下一個匹配的路由,或者直接調用失敗處理器。 - 處理器
請求的數據被驗證成功之后,會作為參數來調用綁定在路由上的處理器。作為應用的主要邏輯,在調用結束后,會返回一個響應。 - 響應
返回的響應也是被處理過的。Rocket 會生成合適的HTTP響應并且發送到客戶端。這樣一個生命周期就結束了。Rocket繼續監聽新的請求。并為每個請求創建一個生命周期。
這個章節剩余的部分會詳述路由部分和Rocket分發請求到請求處理器所需要的組件。之后的章節會詳述請求、響應部分和Rocket的其它組件。
路由
Rocket應用都是以路由和處理器為中心。路由由下列組成:
- 一組參數來匹配新來的請求。
- 一個處理請求和返回響應的處理器。
處理器是一個簡單的函數,接受任意個數的參數并且返回任意類型的結果。
用來匹配的參數包括靜態路徑、動態路徑、路徑參數、表單、查詢參數、特定的請求格式和 請求體數據。Rocket 利用屬性(類似其它語言的裝飾器一樣),使得路由聲明變得加單。路由聲明就是給處理器方法添加注解并且有一組參數用來匹配。一個完整的路由聲明是像這樣的:
#[get("/world")] // <- route attribute
fn world() -> &'static str { // <- request handler
"Hello, world!"
}
這個聲明了world
路由用來匹配靜態路徑"/world"
的GET
請求。"world"
路由比較簡單,不過當構建復雜的應用個的時候,額外的路由參數是必須的。請求 一節里面講解了構建路由的所有情況。
掛載
在Rocket 能夠分發請求到一個路由之前,路由需要先完成掛載。 掛載路由,類似給路由一個命名空間。用 Rocket實例的mount方法來掛載一個路由。Rocket實例通常使用rocket::ignite()靜態方法來創建。
mount
方法需要:
- 一個包含一系列路由的命名空間的路徑。
- 一組對應路由的處理器!和生成Rocket應用代碼的宏。
例如,掛載之前聲明的world
路由,我們可以這樣寫:
rocket::ignite().mount("/hello", routes![world]);
這塊代碼通過 ignite 函數創建了一個新的Rocket實例,并且將 world
路由掛載到了“/hello” 路徑下面。
其結果就是,GET請求 “/hello/world”路徑就會訪問world函數。
命名空間
當一個路由是在root之外的其它模塊里聲明的,你會在掛載的時候會得到一個異常:
mod other {
#[get("/world")]
pub fn world() -> &'static str {
"Hello, world!"
}
}
use other::world;
fn main() {
// error[E0425]: cannot find value `static_rocket_route_info_for_world` in this scope
rocket::ignite().mount("/hello", routes![world]);
}
這個錯誤出現是因為 宏 routes! 在生成Rocket代碼的時候隱式地將 route的名稱 轉換為了當前解構里的名稱。解決方法是在寫路由名稱的時候加上模塊的名字:
rocket::ignite().mount("/hello", routes![other::world]);
運行
現在Rocket已經有了路由,你可以用launch方法來啟動Rocket接受請求。launch用來方法啟動服務等待請求。當請求到達時,Rocket 會找到匹配的路由,并將請求分發到該路由的處理器。
通常情況下我們在main
方法里調用launch
方法?,F在我們已經完成了Hello, world!
程序,看起來像這樣:
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[get("/world")]
fn world() -> &'static str {
"Hello, world!"
}
fn main() {
rocket::ignite().mount("/hello", routes![world]).launch();
}
注意到我們添加了#![feature(plugin)]
和 #![plugin(rocket_codegen)]
這兩行,是告訴Rust我們使用了Rocket的代碼生成插件。同樣我們將通過extern crate rocket
將 rocket
crate(箱)引入了我們的命名空間。最后,我們在main
函數里調用了launch
方法。
運行這個程序,控制臺會顯示內容:
?? Configured for development.
=> address: localhost
=> port: 8000
=> log: normal
=> workers: [logical cores * 2]
=> secret key: generated
=> limits: forms = 32KiB
=> tls: disabled
?? Mounting '/hello':
=> GET /hello/world
?? Rocket has launched from http://localhost:8000
我們訪問 localhost:8000/hello/world
,就會看到Hello, world
, 正好是我們預期的。
在GitHub上有這個例子一個完整版的crate(箱),只要cargo run
就能運行。 你可以在 GitHub examples directory 中找到更多的例子,涵蓋了所有Rocket的特性。