在這個章節中,將會介紹rocket_contrib這個Rocket官方的擴充套件,它針對幾個比較特殊但也算常見的使用案例,提供額外的結構體甚至是子框架。在處理HTTP請求的方面,rocket_contrib可以提供JSON、MessagePack和UUID格式的支援,以及靜態檔案(如圖片檔、CSS、JS等等的靜態資源檔)的提供。



JSON

rocket_contribjson模組提供了Json結構體,可以用來序列化或是反序列化JSON格式的資料。rocket_contrib::json::Json結構體可以直接當作資料守衛或是路由處理程序的函式的回傳值型別,有關於回傳JSON的用法會在之後的章節再做介紹。

若要使用rocket_contribjson模組,必須要在Cargo程式專案的設定檔中啟用rocket_contribjson特色。如下:

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["json"]

我們可以替自己實作出來的型別加上Deserialize這個derive屬性的參數,使這個型別的資料可以直接被Rocket框架反序列化。使用rocket_contrib::json::Json結構體來包裹可被Rocket框架反序列化的資料型別,就可以將JSON字串直接反序列化成該型別了!而且我們可以把這個Json<T>型別直接當作資料守衛來使用。

例如:

#[macro_use]
extern crate serde_derive;
extern crate rocket_contrib;

use rocket_contrib::json::Json;

#[derive(Deserialize)]
struct User {
    id: i32,
    name: String,
}

#[post("/?hello", data = "<user>")]
fn handler(user: Json<User>) -> String {
    format!("Hello, world! id = {}, name = {}", user.id, user.name)
}

以上程式,當HTTP請求的主體中的資料是JSON格式的字串時,就會被反序列化為User結構實體。

這邊要注意的地方是,Deserialize這個derive屬性的參數來自於serde_derive這個crate。在啟用rocket_contribjson特色時,記得也要將serdeserde_derive這兩個crate給引用至程式專案中。

為了避免傳進來的JSON格式的字串太長,而導致反序列化需要耗費大量時間與記憶體,我們可以利用先前學過的Rocket框架的設定方式,在limits設定項目中加入json欄位來限制資料量。

MessagePack

rocket_contribmsgpack模組提供了MsgPack結構體,可以用來序列化或是反序列化MessagePack格式的資料。rocket_contrib::msgpack::MsgPack結構體可以直接當作資料守衛或是路由處理程序的函式的回傳值型別,有關於回傳MsgPack的用法會在之後的章節再做介紹。

若要使用rocket_contribmsgpack模組,必須要在Cargo程式專案的設定檔中啟用rocket_contribmsgpack特色。如下:

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["msgpack"]

我們可以替自己實作出來的型別加上Deserialize這個derive屬性的參數,使這個型別的資料可以直接被Rocket框架反序列化。使用rocket_contrib::msgpack::MsgPack結構體來包裹可被Rocket框架反序列化的資料型別,就可以將符合MessagePack格式的資料直接反序列化成該型別了!而且我們可以把這個MsgPack<T>型別直接當作資料守衛來使用。

例如:

#[macro_use]
extern crate serde_derive;
extern crate rocket_contrib;

use rocket_contrib::msgpack::MsgPack;

#[derive(Deserialize)]
struct User {
    id: i32,
    name: String,
}

#[post("/?hello", data = "<user>")]
fn handler(user: MsgPack<User>) -> String {
    format!("Hello, world! id = {}, name = {}", user.id, user.name)
}

以上程式,當HTTP請求的主體中的資料是符合MessagePack格式的資料時,就會被反序列化為User結構實體。

這邊要注意的地方是,Deserialize這個derive屬性的參數來自於serde_derive這個crate。在啟用rocket_contribjson特色時,記得也要將serdeserde_derive這兩個crate給引用至程式專案中。

為了避免傳進來的MessagePack格式的資料太長,而導致反序列化需要耗費大量時間與記憶體,我們可以利用先前學過的Rocket框架的設定方式,在limits設定項目中加入msgpack欄位來限制資料量。

UUID

rocket_contribuuid模組提供了Uuid結構體,可以用來序列化或是反序列化UUID格式的資料。rocket_contrib::uuid::Uuid結構體可以直接當作參數守衛,並且它還實作了request::FromFormValue特性,所以可以直接使用request::Form<T>或是request::LenientForm<T>來包裹成查詢守衛或是資料守衛。

若要使用rocket_contribuuid模組,必須要在Cargo程式專案的設定檔中啟用rocket_contribuuid特色。如下:

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["uuid"]

rocket_contrib::uuid::Uuid結構體的使用方式不難,例如以下程式:

extern crate rocket_contrib;

use rocket::response::status;
use rocket_contrib::uuid::Uuid;

#[get("/device/<uuid>/name")]
fn handler(uuid: Option<Uuid>) -> Result<&'static str, status::NotFound<&'static str>> {
    match uuid {
        Some(uuid) => match uuid.to_fields_le() {
            (1692521986, 53139, 32579, [149, 69, 236, 56, 53, 249, 44, 216]) => Ok("Vacuum Cleaner"), // 02d6e164-93cf-437f-9545-ec3835f92cd8
            (613895186, 12848, 3396, [160, 28, 6, 3, 61, 174, 199, 229]) => Ok("Refrigerator"), // 124c9724-3032-440d-a01c-06033daec7e5
            _ => Err(status::NotFound("Unknown device"))
        },
        None => Err(status::NotFound("Incorrect UUID"))
    }
}

靜態檔案

在先前的章節中我們有提到可以使用區段守衛搭配Rocket內建的response::NamedFile結構體,來讓Rocket實現出提供檔案系統的某目錄下所有靜態檔案(如圖片檔、CSS、JS等等的靜態資源檔)的功能。rocket_contrib套件針對這個功能提供了更方便serve模組。

若要使用rocket_contribserve模組,必須要在Cargo程式專案的設定檔中啟用rocket_contribserve特色。如下:

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["serve"]

原先我們得自行實作路由處理程序來實現這個功能,但有了rocket_contribserve模組後就不需要了,只要將rocket_contrib::serve::StaticFiles結構實體透過Rocket結構實體的mount方法註冊給Rocket使用即可。

例如:

extern crate rocket_contrib;

use rocket_contrib::serve::StaticFiles;
    
fn main() {
    let rocket = rocket::ignite();

    let rocket = rocket.mount("/public", StaticFiles::from("static"));
    
    rocket.launch();
}

以上程式,可以使/public這個路徑底下的相對路徑對應到Rocket應用程式的工作目錄中的static目錄下的檔案。與使用路由處理程序的函數來回傳response::NamedFile時一樣,使用rocket_contrib::serve::StaticFiles結構體也會在HTTP回應中自動加入Content-Type欄位。這個Content-Type欄位的值會根據網址路徑指到的檔案類型(依照檔案副檔名)而自動做適當的變化。

篩選HTTP請求的主體類型

Rocket框架可以依照HTTP請求中的Content-Type欄位,使路由處理程序只允許某種特定類型的HTTP請求。要做到這件事,只需要在路由處理程序的HTTP請求方法的屬性中,加上format參數,與data參數搭配使用。

例如以下程式,可以使Rocket有能力對相同的路徑但不同資料類型的HTTP請求做不一樣的處理。

#[macro_use]
extern crate serde_derive;
extern crate rocket_contrib;
    
use rocket::request::Form;
use rocket_contrib::json::Json;
use rocket_contrib::msgpack::MsgPack;
    
#[derive(FromForm, Deserialize)]
struct User {
    id: i32,
    name: String,
}
    
#[post("/?hello", format = "application/json", data = "<user>")]
fn handler_1(user: Json<User>) -> String {
    format!("Hello, world! id = {}, name = {} (from JSON)", user.id, user.name)
}
    
#[post("/?hello", format = "application/msgpack", data = "<user>")]
fn handler_2(user: MsgPack<User>) -> String {
    format!("Hello, world! id = {}, name = {} (from MessagePack)", user.id, user.name)
}

#[post("/?hello", format = "form", data = "<user>")]
fn handler_3(user: Form<User>) -> String {
    format!("Hello, world! id = {}, name = {} (from Form)", user.id, user.name)
}

format參數不一定要寫出完整的MIME類型,以下是它的簡寫對應表:

MIME類型簡寫
application/octet-streambinary
application/jsonjson
application/msgpackmsgpack
application/x-www-form-urlencodedform
application/javascriptjs
text/htmlhtml
text/plainplain
text/csscss
text/xmlxml
multipart/form-datamultipart

總結

這個章節介紹了rocket_contrib這個Rocket官方提供的擴充套件中,有關處理HTTP請求的功能。下一個章節會先暫時拋開rocket_contrib,要來介紹Rocket框架的狀態(state)處理機制。

下一章:狀態(State)