iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
Software Development

Rust Web API 從零開始系列 第 22

Day22 - 訂閱確認,再論SeaORM中的ActiveModel

  • 分享至 

  • xImage
  •  

目前已經完成訂閱的功能,當使用者登記的時候會收到一封驗證信,點入信中的連結便會將訂閱狀態改為啟用。

Handler

首先我們就來新增一個handler吧:

pub async fn confirm(
    state: State<AppState>,
    Path(token): Path<Uuid>,
) -> StatusCode {
    todo!();
}

首先我們要從路由中取得token的資料,所以要用到Path提取器,再來要對資料庫做異動,因次也要取得AppState。

pub async fn confirm(
    state: State<AppState>,
    Path(token): Path<Uuid>,
) -> Result<StatusCode,StatusCode> {
    let subscription = subscription_tokens::Entity::find()
        .find_also_related(Subscriptions)
        .filter(subscription_tokens::Column::SubscriptionToken.eq(token.to_string()))
        .one(&state.database)
        .await
        //// 處理非同步查詢的錯誤,若沒有錯誤但查不到資料會回傳None
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
        //// 處理查不到資料的情況,將None轉換成Result中的Err
        .ok_or(StatusCode::NOT_FOUND)?
        .1
        .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
        
        todo!();
}

seaORM中要將關聯的資料取出需要使用find_also_related(),因為在資料庫有設定了關聯,便可以從token反查回原來的訂閱紀錄。
後面有一連串的調用,可以看到rust中對於錯誤處理強大的支援,在C#中我們往往會忽略掉所有可能出錯或者可能為null的edge case,假定一切都會運行正常。在rust中將錯誤與不存在顯式的以ResultOption型別暴露出來,強調在程式面必須處理,寫起來較為繁瑣,但確保了程式的穩定性,事實上為了處裡這些細節,rust也提供了很多的工具來處理ResultOption
目前查出來的資料只是普通的Model,無法用於資料更新,這時候就要介紹到seaORM中的重要概念ActiveModel,只有將Model轉換ActiveModel才能夠更新,透過這個設計也可以避免Rust中的資料競爭問題,將可變與不可變做出明確的界線,使用上也十分簡單:

pub async fn confirm(
    state: State<AppState>,
    Path(token): Path<Uuid>,
) -> Result<StatusCode,StatusCode> {
    let subscription = subscription_tokens::Entity::find()
        .find_also_related(Subscriptions)
        .filter(subscription_tokens::Column::SubscriptionToken.eq(token.to_string()))
        .one(&state.database)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
        .ok_or(StatusCode::NOT_FOUND)?
        .1
        .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
        //// 轉換成active model
        .into_active_model();
        
    subscription.status = Set(SubscriptionStatus::Active);

    subscription.update(&state.database).await?;

    Ok(StatusCode::OK)
}

最後再把handler加入Router中就好了

//// application.rs
pub fn build() -> Router {
    let database = get_database(&config.database).await.unwrap();
    let email_client = get_email_client(&config.email_client);
        
    let app = Router::new()
        .route("/", get(health_check))
        .route("/subscriptions", post(subscribe))
        //// 加入認證的API路徑
        .route("/subscriptions/confirm/:token", get(confirm))
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi()))
        .with_state(AppState {
            database,
            email_client
        });;
    app
}

上一篇
Day21 - 整合認證信寄送
下一篇
Day23 - 權限驗證(1) - JWT Handler
系列文
Rust Web API 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言