Rust登录注册模块

Rust04-登录注册模块

用户模块的介绍

接下来我们用 actix-web + sqlx + MySQL写一个完整的用户注册登录模块

👉 用户模块

先来看看我们的模块结构是什么样子的

javascript 复制代码
src/
├── main.rs
├── database/
│   └── mod.rs
└── modules/
    ├── mod.rs
    └── user/
        ├── mod.rs
        ├── routes.rs
        ├── models.rs      # 用户数据模型
        ├── services.rs    # 业务逻辑
        └── handlers.rs    # 处理函数(可选)
🍎配置插件bcrypt

Cargo.toml之中添加依赖插件

javascript 复制代码
bcrypt = "0.15"
🍎main.rs 注册模块

main.rs入口文件之中引入模块

javascript 复制代码
mod common {
    pub mod response;
}
mod modules {
    pub mod user;
}

use actix_cors::Cors;
use actix_web::{App, HttpServer, Responder,HttpResponse,web};
use dotenv::dotenv;
use sqlx::MySqlPool;
use std::env;

async fn welcome() -> impl Responder {
    let addr = "127.0.0.1:8080/api";
    println!("服务已经启动,数据库已连接,地址为:{}", addr);
    HttpResponse::Ok().body(format!("欢迎访问NexusRust API,服务地址:{}", addr))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok(); // 一定要在读取环境变量之前调用

    let database_url = env::var("DATABASE_URL").unwrap(); // 获取数据库连接字符串
    let pool = MySqlPool::connect(&database_url).await.unwrap();
    
    HttpServer::new(move || {
        let cors = Cors::default()
            .allow_any_origin()
            .allow_any_method()
            .allow_any_header(); // 允许所有来源
        App::new()
            // 添加 CORS 中间件
            .wrap(cors)
            // 2. 注入数据库连接池
            .app_data(web::Data::new(pool.clone()))
            // 3. 注册模块路由加前缀
            .service(
                web::scope("/api") // 这里加上 /api 前缀
                    .configure(modules::user::routes::config),
            )
            // 3. 注册路由
            .route("/", web::get().to(welcome))
    })
    .bind("127.0.0.1:8888")?
    .run()
    .await
}
🍎user=>mod.rs 模块入口

组织声明子模块,在模块入口文件之中进行声明组织子模块

javascript 复制代码
pub mod handlers;
pub mod models;
pub mod routes; // 必须有这一行,否则无法使用路由
🍎models.rs

声明该模块对应的数据库字段的类型,当然了,这个并不是必须的,更像是我们数据的类型

javascript 复制代码
// # 用户数据模型
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
pub struct User {
    pub id: i64,
    pub name: String,
    pub email: String,
}

👉用户注册/register

接下来我们开始从用户注册接口写我们的整个服务部分,一开始可能会略微简单一些,到后面我们不断的进行优化和升级

🍎src\modules\user\routes.rs

先添加我们的路由接口

javascript 复制代码
pub fn config(cfg: &mut web::ServiceConfig) {
  cfg.route("/register", web::post().to(crate::modules::user::handlers::register_users));
  cfg.route("/users", web::get().to(crate::modules::user::handlers::get_all_users));
}
🍎src\common\response.rs

添加一个公共模块存放我们返回的数据类型

javascript 复制代码
use serde::Serialize;

#[derive(Serialize)]
pub struct ApiResponse {
    pub code: i32,
    pub msg: &'static str,
}
🍎 在main.rs之中声明
javascript 复制代码
mod common {
    pub mod response;
}
🍎 在handlers.rs之中定义并使用

引入

javascript 复制代码
use crate::common::response::ApiResponse;// 导入 ApiResponse 模型

使用

javascript 复制代码
// use crate::modules::user::models::ApiResponse; 
use bcrypt::{hash, DEFAULT_COST};
// use serde::Serialize;
use crate::common::response::ApiResponse;// 导入 ApiResponse 模型

// 注册请求体
#[derive(serde::Deserialize)]
pub struct RegisterRequest {
    pub username: String,
    pub password: String,
    // 其他字段可选
}

// use actix_web::web;
pub async fn register_users(
    pool: web::Data<MySqlPool>,
    form: web::Json<RegisterRequest>,
) -> HttpResponse {
    // 检查用户名是否已存在
    let exists: (i64,) = match sqlx::query_as("SELECT COUNT(*) FROM sys_user WHERE username = ?")
        .bind(&form.username)
        .fetch_one(pool.get_ref())
        .await
    {
        Ok(count) => count,
        Err(_) => {
            return HttpResponse::InternalServerError().json(ApiResponse {
                code: 500,
                msg: "数据库错误",
            })
        }
    };
    if exists.0 > 0 {
        return HttpResponse::Ok().json(ApiResponse {
            code: 400,
            msg: "用户名已存在",
        });
    }

    // 密码加密
    let hashed_pwd = match hash(&form.password, DEFAULT_COST) {
        Ok(pwd) => pwd,
        Err(_) => {
            return HttpResponse::InternalServerError().json(ApiResponse {
                code: 500,
                msg: "密码加密失败",
            })
        }
    };

    // 插入新用户
    let result = sqlx::query("INSERT INTO sys_user (username, password) VALUES (?, ?)")
        .bind(&form.username)
        .bind(&hashed_pwd)
        .execute(pool.get_ref())
        .await;

    match result {
        Ok(_) => HttpResponse::Ok().json(ApiResponse {
            code: 200,
            msg: "注册成功",
        }),
        Err(e) => {
            eprintln!("注册失败: {:?}", e);
            HttpResponse::InternalServerError().json(ApiResponse {
                code: 500,
                msg: "注册失败",
            })
        }
    }
}
🍎测试接口
javascript 复制代码
//成功的时候返回
{
    "code": 200,
    "msg": "注册成功"
}

//失败的时候返回的数据
{
    "code": 400,
    "msg": "用户名已存在"
}

👉用户登录/login

接下来我们开始写我们登录的接口模块

🍎src\modules\user\routes.rs

先添加我们的路由接口,还是在我们之前的文件之中

javascript 复制代码
pub fn config(cfg: &mut web::ServiceConfig) {
  cfg.route("/register", web::post().to(crate::modules::user::handlers::register_users));
  cfg.route("/login", web::post().to(crate::modules::user::handlers::login_users));
}
🍎 在handlers.rs之中引入
javascript 复制代码
use bcrypt::{hash, DEFAULT_COST};// 导入 bcrypt 库

编写登录接口

javascript 复制代码
// 登录接口
pub async fn login_users(
    pool: web::Data<MySqlPool>,
    form: web::Json<LoginRequest>,
) -> HttpResponse {
    // 查询用户
    let user = sqlx::query_as::<_, User>("SELECT * FROM sys_user WHERE username = ?")
        .bind(&form.username)
        .fetch_one(pool.get_ref())
        .await;

    let user = match user {
        Ok(u) => u,
        Err(_) => {
            return HttpResponse::Ok().json(ApiResponse {
                code: 400,
                msg: "用户名或密码错误",
            });
        }
    };

    // 校验密码
    let is_valid = verify(&form.password, &user.password).unwrap_or(false);

    if is_valid {
        HttpResponse::Ok().json(ApiResponse {
            code: 200,
            msg: "登录成功",
        })
    } else {
        HttpResponse::Ok().json(ApiResponse {
            code: 400,
            msg: "用户名或密码错误",
        })
    }
}
🍎测试接口
javascript 复制代码
// 失败的时候返回格式
{
    "code": 400,
    "msg": "用户名或密码错误"
}


//成功的时候返回格式
{
    "code": 200,
    "msg": "登录成功"
}

登录接口ok

相关推荐
vx_bisheyuange1 分钟前
基于SpringBoot的便利店信息管理系统
前端·javascript·vue.js·毕业设计
晚烛2 分钟前
智启工厂脉搏:基于 OpenHarmony + Flutter 的信创工业边缘智能平台构建实践
前端·javascript·flutter
Zsnoin能5 分钟前
都快2026了,还有人不会国际化和暗黑主题适配吗,一篇文章彻底解决
前端·javascript
两个西柚呀6 分钟前
es6和commonjs模块化规范的深入理解
前端·javascript·es6
www_stdio6 分钟前
爬楼梯?不,你在攀登算法的珠穆朗玛峰!
前端·javascript·面试
光影少年7 分钟前
RN vs Flutter vs Expo 选型
前端·flutter·react native
风止何安啊14 分钟前
🚀别再卷 Redux 了!Zustand 才是 React 状态管理的躺平神器
前端·react.js·面试
鹿角片ljp19 分钟前
Spring Boot Web入门:从零开始构建web程序
前端·spring boot·后端
向下的大树25 分钟前
Vue 2迁移Vue 3实战:从痛点到突破
前端·javascript·vue.js
我很苦涩的27 分钟前
原生小程序使用echarts
前端·小程序·echarts