域名 AXUM.RS 将于 2025 年 10 月到期。我们无意再对其进行续费,如果你有意接续这个域名,请与我们取得联系。
  • AXUM.RS 现仅需人民币 3000 元(大写:叁仟元整。接受适度议价
  • 按照行业规则,AXUM.RS 到期后,大概率会进入长时间的赎回期,该期间内,如果你想拥有该域名,将要付出高额的费用
  • 我们已启用 AXUM.EU.ORG 域名,并将持续运营
  • 仅接受微信或支付宝交易
如果你对 AXUM.RS 有兴趣,请和我们进行联系:

实现用户注册与激活

本章将实现最终的功能:用户注册,并发送激活邮件。

handler

我们增加几个 handler,其它都过于简单这里不再赘述。重点来看一下注册时的 handler:

// src/handler.rs

pub async fn register(
    Extension(state): Extension<Arc<AppState>>,
    Form(frm): Form<form::RegisterForm>,
) -> Result<(StatusCode, HeaderMap, ())> {
    let cfg = &state.cfg;
    let active_code = model::user::ActiveCode {
        code: gen_active_code(&frm.email),
        email: frm.email,
        email_cfg: cfg.email.clone(),
    };

    let payload = serde_json::to_string(&active_code).map_err(Error::from)?;

    // 发送消息
    topic::send(
        &cfg.rabbitmq.dsn,
        &cfg.rabbitmq.exchange_name,
        &cfg.rabbitmq.queue_name,
        &cfg.rabbitmq.routing_key,
        &payload,
    )
    .await
    .map_err(Error::from)?;

    redirect("/active")
}
  • 我们将 model::user::ActiveCode 序列化为 JSON 字符串,然后发送到 RabbitMQ 中。你或许有疑问。为什么这个结构体中要包含邮箱配置项(email_cfg字段)?那是因为在 lapin 的消费者回调中,无法操作外部变量,所以我们需要把那些业务上所需的一些数据全部通过消息队列发送。

消息消费者(发送邮件验证码)

// src/main.rs

#[tokio::main]
async fn main() {
   // ...

    tokio::spawn(send_active_code(cfg.clone()));
	
    // ...
}

async fn send_active_code(cfg: Config) {
    topic::receive(
        &cfg.rabbitmq.dsn,
        &cfg.rabbitmq.exchange_name,
        &cfg.rabbitmq.queue_name,
        &cfg.rabbitmq.routing_key,
        "MAIL",
        move |delivery: DeliveryResult| async move {
            tracing::debug!("aaa");
            let delivery = match delivery {
                Ok(Some(delivery)) => delivery,
                Ok(None) => {
                    tracing::error!("None ");
                    return;
                }
                Err(err) => {
                    tracing::error!("Failed to consume queue message {}", err);
                    return;
                }
            };

            let message = String::from_utf8_lossy(&delivery.data);
            tracing::info!("Received a message: {}", message);

            let ac: model::user::ActiveCode = serde_json::from_str(&message).unwrap();
            let from = ac.email_cfg.username.clone();
            let resp = email::send(
                ac.email_cfg,
                model::email::Email {
                    from,
                    to: ac.email,
                    subject: format!("激活账号"),
                    body: format!("你的激活码是:{}", ac.code),
                },
            )
            .await
            .unwrap();

            tracing::info!("Send email: {:?}", resp);
            delivery.ack(BasicAckOptions::default()).await.unwrap();
        },
    )
    .await
    .unwrap();
}

main() 函数中,我们使用 tokio::spawn(send_active_code(cfg.clone())); 让消息的消费者在一个新的异步任何中执行

而在 send_active_code() 函数的消费者回调中

  • let message = String::from_utf8_lossy(&delivery.data):获取消息数据,并转换成字符串

  • let ac: model::user::ActiveCode = serde_json::from_str(&message).unwrap():将消息数据反序列化为 model::user::ActiveCode

  • 最后就是发送邮件

    let resp = email::send(
                ac.email_cfg,
                model::email::Email {
                    from,
                    to: ac.email,
                    subject: format!("激活账号"),
                    body: format!("你的激活码是:{}", ac.code),
                },
            )
            .await
            .unwrap();
    
  • 进入你填写的邮箱,你应该能收到如图所示的邮件:

let message = String::from_utf8_lossy(&delivery.data):获取消息数据,并转换成字符串

let ac: model::user::ActiveCode = serde_json::from_str(&message).unwrap():将消息数据反序列化为 model::user::ActiveCode

最后就是发送邮件

进入你填写的邮箱,你应该能收到如图所示的邮件:

要查看完整内容,请先登录