在這個章節中,將會介紹Rocket框架處理JSON和MessagePack請求的方式,以及Rocket框架內建的處理UUID的做法,還有提供靜態檔案(如圖片檔、CSS、JS等等的靜態資源檔)的更好方式。



JSON

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

若要使用serde::json::Json結構體,必須要在Cargo程式專案的設定檔中啟用rocketjson特色,同時加入serde這個crate,並啟用derive特色。如下:

[dependencies]
serde = { version = "1", features = ["derive"] }

rocket = { version = "0.5.0", features = ["json"] }

有關於serde的介紹可以參考這篇文章:

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

例如:

#[macro_use]
extern crate serde;

#[macro_use]
extern crate rocket;

use rocket::serde::json::Json;

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

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

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![handler])
}

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

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

MessagePack

Rocket提供的serde::msgpack::MsgPack結構體可以直接當作資料守衛或是路由處理程序的函式的回傳值型別,有關於回傳MsgPack的用法會在之後的章節再做介紹。

serde::json::Json結構體一樣,若要使用serde::msgpack::MsgPack結構體,必須要在Cargo程式專案的設定檔中啟用rocketmsgpack特色,同時加入serde這個crate,並啟用derive特色。

使用rocket_contrib::msgpack::MsgPack結構體來包裹可被serde反序列化的資料型別,就可以將符合MessagePack格式的資料直接反序列化成該型別了!而且我們可以把這個MsgPack<T>型別直接當作資料守衛來使用。

使用serde::msgpack::MsgPack結構體來包裹可被serde反序列化的資料型別,就可以將MessagePack格式的資料直接反序列化成該型別了!而且我們可以把這個MsgPack<T>型別直接當作資料守衛來使用。

例如:

#[macro_use]
extern crate serde;

#[macro_use]
extern crate rocket;

use rocket::serde::msgpack::MsgPack;

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

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

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![handler])
}

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

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

UUID

Rocket提供的serde::uuid::Uuid結構體可以直接當作查詢守衛或是資料守衛。

若要使用serde::uuid::Uuid結構體,必須要在Cargo程式專案的設定檔中啟用rocketuuid特色。

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

#[macro_use]
extern crate rocket;

use rocket::{response::status, serde::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]) => {
                // 02d6e164-93cf-437f-9545-ec3835f92cd8
                Ok("Vacuum Cleaner")
            },
            (613895186, 12848, 3396, [160, 28, 6, 3, 61, 174, 199, 229]) => {
                // 124c9724-3032-440d-a01c-06033daec7e5
                Ok("Refrigerator")
            },
            _ => Err(status::NotFound("Unknown device")),
        },
        None => Err(status::NotFound("Incorrect UUID")),
    }
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![handler])
}

靜態檔案

在先前的章節中我們有提到可以使用區段守衛搭配Rocket內建的fs::NamedFile結構體,來讓Rocket實現出提供檔案系統的某目錄下所有靜態檔案(如圖片檔、CSS、JS等等的靜態資源檔)的功能。但Rocket其實也有提供fs::FileServer結構體來幫我們快速實作出這個功能,只要將fs::FileServer結構實體透過Rocket結構實體的mount方法註冊給Rocket使用即可。

例如:

#[macro_use]
extern crate rocket;

use rocket::fs::FileServer;

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/public", FileServer::from("static"))
}

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

篩選HTTP請求的主體類型

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

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

#[macro_use]
extern crate serde;

#[macro_use]
extern crate rocket;

use rocket::{
    form::Form,
    serde::{json::Json, msgpack::MsgPack},
};

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

#[post("/", format = "application/json", data = "<user>")]
fn handler_1(user: Json<User>) -> String {
    format!("Hello, world! id = {}, name = {} (from JSON)", user.id, user.name)
}

#[post("/", format = "application/msgpack", data = "<user>")]
fn handler_2(user: MsgPack<User>) -> String {
    format!("Hello, world! id = {}, name = {} (from MessagePack)", user.id, user.name)
}

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

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![handler_1, handler_2, handler_3])
}

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

MIME類型 簡寫
application/octet-stream binary
application/json json
application/msgpack msgpack
application/x-www-form-urlencoded form
application/javascript js
text/html html
text/plain plain
text/css css
text/xml xml
multipart/form-data multipart

總結

這個章節介紹了Rocket框架與serde框架結合起來處理HTTP請求的用法。下一個章節會先暫時拋開serde,要來介紹Rocket框架的狀態(state)處理機制。

下一章:狀態(State)