在前三天,我們建立了專案的基礎並成功地整合了 QR Code 產生功能至 Actix Web 中。今天,我們要著重於如何進一步加強與使用者的互動性。
目前,專案中的 QR Code 是固定導向到 https://buckychu.im
,但如果能夠讓使用者自行輸入想要產生的 QR Code 連結,那會更有實用性。
為此,我們可以修改 index
函式來接受 query parameters:
#[get("/{data}")]
async fn index(data: web::Path<String>) -> HttpResponse {
let code = QrCode::new(data.as_str().as_bytes()).unwrap();
// ... 其餘部分不變 ...
}
執行 cargo run
後,然後試試看在瀏覽器 URL 輸入 http://127.0.0.1:8080/hello
,就可以看到以下圖片
在上面的範例,使用了 unwrap()
來取得 QR Code。但是,這樣的作法在實際應用上並不安全,因為任何錯誤都會導致整個程式崩潰。為了更友善地處理錯誤,我們可以使用 Rust 的 match
來處理:
let code = match QrCode::new(data.as_str().as_bytes()) {
Ok(c) => c,
Err(_) => return HttpResponse::BadRequest().body("你輸入的字串無法處理"),
};
這樣一來,任何產生 QR Code 的錯誤都會回傳一個 400 Bad Request 的回應。
不過,現在如果在瀏覽器輸入 http://127.0.0.1:8080/https://buckychu.im
會很明顯的出錯,這應該不是我們想要的,畢竟一般會使用 QR code 應該就是要讓使用者可以輸入網址才對。
這個問題主要是因為 /
在 URL 中是有特殊的意義,所以當嘗試顯示 http://127.0.0.1:8080/https://buckychu.im
這樣的網址時,會解讀成多個路徑片段,而不是一個單一的路徑參數。
解決這個問題,有蠻多解決的方式,這裡我們試著改變路由的設計來處理。
這裡會使用 query string 來傳遞網址,這樣就不需要擔心路徑分隔符的問題。
首先需要先安裝 serde 這個套件,主要會幫我們處理 JSON 格式,這個在之後也是很重要的部分。
在 Terminal 輸入以下指令:
cargo add serde -F derive
然後建立一個 Info 的結構,並用 serde 標記處理:
#[derive(serde::Deserialize)]
struct Info {
url: String,
}
接著改變一下原本的 index()
:
#[get("/generate_qr")]
async fn index(data: web::Query<Info>) -> HttpResponse {
let code = match QrCode::new(data.url.as_bytes()) {
Ok(c) => c,
Err(_) => return HttpResponse::BadRequest().body("你輸入的字串無法處理"),
};
let image = code.render::<Luma<u8>>().build();
let mut buffer = Vec::new();
match DynamicImage::ImageLuma8(image).write_to(&mut buffer, image::ImageOutputFormat::Png) {
Ok(_) => (),
Err(_) => return HttpResponse::InternalServerError().body("無法生成圖像"),
}
HttpResponse::Ok().content_type("image/png").body(buffer)
}
然後在瀏覽器輸入 http://127.0.0.1:8080/generate_qr?url=https://buckychu.im
就可以產生以下的 QR code 圖片了。
今天,我們加強了這個專案的靈活性,並增加了使用者互動性。現在,我們的服務不僅可以生成 QR Code,還能根據使用者的需求來定制。明天,我們來試圖加上改變顏色的功能,敬請期待!