接下來要進一步完善訂閱的功能,當使用者送出訂閱資料時,希望能夠寄送認證信以確認訂閱是有效的,我選擇使用smtp2go
的第三方服務來寄送通知信。
在rust要呼叫外部的API可以使用reqwest
這個套件。
cargo add reqwest
我們先看一下它的簡單範例
let body = reqwest::get("https://www.rust-lang.org")
.await?
.text()
.await?;
reqwest支援非同步呼叫,並且只要簡單的使用get就可以取得需要的資料。但在應用程式中呼叫第三方API應該要考慮連線數量的問題,官方建議使用client
來處裡,我們可以建立一個client
並且重複使用,client
內部會建立連線池來控管對外API的連線。此外client內部已經自動以Arc
實做以利重用,在rust中以Arc
包裝的資料就像C#中的參考一樣,在複製的時候只是增加引用計數而不是真的複製一個物件。
為了方便使用,我們將reqwest
的client用自定義的結構包起來,並且建立一個建構式:
#[derive(Debug, Clone)]
pub struct EmailClient {
http_client: Client,
base_url: Url,
sender: SubscriberEmail,
api_key: Secret<String>,
}
impl EmailClient {
pub fn new(
base_url: String,
sender: SubscriberEmail,
api_key: Secret<String>,
timeout: std::time::Duration,
) -> Self {
let http_client = Client::builder().timeout(timeout).build().unwrap();
Self {
http_client,
base_url: base_url.parse().unwrap(),
sender,
api_key,
}
}
}
其中的base_url
的型別是透過url
這個套件提供,能夠方便的進行網址的解析與組合。接下來就是設定一個簡單的寄信方法,這邊就不贅述了。
對於reqwest client的測試,我們需要使用tokio
提供對非同步方法的測試,並且透過MockServer
來模擬服務接收到的訊息,首先安裝wiremock:
cargo add --dev wiremock
wiremock
提供了一系列的組件來協助進行測試物件的替換。--dev
安裝的套件只會在測試時使用,release
模式編譯的時候會被忽略。
#[tokio::test]
async fn send_email_fires_a_request_to_base_url() {
// Arrange
let mock_server = MockServer::start().await;
let token = api_key();
let email_client = email_client(mock_server.uri(), token.clone());
Mock::given(method("POST"))
.and(header("Content-Type", "application/json"))
.and(path("/v3/email/send"))
.and(SendEmailBodyMatcher)
.respond_with(ResponseTemplate::new(200))
.expect(1)
.mount(&mock_server)
.await;
// Act
let outcome = email_client
.send_email(&email(), &subject(), &content())
.await;
// Assert
assert_ok!(outcome);
}
使用上就是一般測試的寫法,啟動MockServer
並且設定收到訊息時的回應以及預計收到的次數就可以了。