首页 > 其他分享 >Rust使用Actix-web和SeaORM开发WebAPI通过Swagger UI查看接口文档

Rust使用Actix-web和SeaORM开发WebAPI通过Swagger UI查看接口文档

时间:2024-09-13 21:12:58浏览次数:12  
标签:WebAPI web Actix description db pub user id

本文将介绍Rust语言使用Actix-web和SeaORM库,数据库使用PostgreSQL,开发增删改查项目,同时可以通过Swagger UI查看接口文档和查看标准Rust文档

开始项目

首先创建新项目,名称为rusty_crab_api

cargo new rusty_crab_api

Cargo.toml

[dependencies]
sea-orm = { version = "1.0.0-rc.5", features = [ "sqlx-postgres", "runtime-tokio-native-tls", "macros" ] }
tokio = { version = "1.35.1", features = ["full"] }
chrono = "0.4.33"
actix-web = "4.4.0"
serde = { version = "1.0", features = ["derive"] }
utoipa = { version = "4", features = ["actix_extras"] }
utoipa-swagger-ui = { version = "4", features = ["actix-web"] }
serde_json = "1.0"

使用SeaORM作为ORM工具,它提供了sea-orm-cli​工具,方便生成entity

PostgreSQL创建数据库

CREATE TABLE "user" (
  id SERIAL PRIMARY KEY,
  username VARCHAR(32) NOT NULL,
  birthday TIMESTAMP,
  sex VARCHAR(10),
  address VARCHAR(256)
);

COMMENT ON COLUMN "user".username IS '用户名称';
COMMENT ON COLUMN "user".birthday IS '生日';
COMMENT ON COLUMN "user".sex IS '性别';
COMMENT ON COLUMN "user".address IS '地址';

安装sea-orm-cli

cargo install sea-orm-cli

生成entity

sea-orm-cli generate entity -u postgres://[用户名]:[密码]@[IP]:[PORT]/[数据库] -o src/entity

自动帮我们生成src./entity/user.rs文件

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "user")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub username: String,
    pub birthday: Option<DateTime>,
    pub sex: Option<String>,
    pub address: Option<String>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

接下来编写接口函数,新建src/handlers/user.rs,编写用户表的增删改查代码,同时在代码文件中编写说明文档,提供给Rust标准文档和Swagger UI使用

/// 表示创建新用户的请求体结构
#[derive(Debug, Deserialize, ToSchema)]
#[schema(example = json!({
    "username": "johndoe",
    "birthday": "2023-09-09T15:53:00",
    "sex": "male",
    "address": "123 Main St, Anytown, USA"
}))]
pub struct CreateUser {
    /// 用户名  
    pub username: String,
    /// 生日(可选)
    #[schema(value_type = String)]
    pub birthday: Option<DateTime>,
    /// 性别(可选)
    pub sex: Option<String>,
    /// 地址(可选)
    pub address: Option<String>,
}
/// 创建新用户
///
/// # 请求体
///
/// 需要一个JSON对象,包含以下字段:
/// - `username`: 字符串,用户名(必填)
/// - `birthday`: ISO 8601格式的日期时间字符串,用户生日(可选)
/// - `sex`: 字符串,用户性别(可选)
/// - `address`: 字符串,用户地址(可选)
///
/// # 响应
///
/// - 成功:返回状态码200和新创建的用户JSON对象
/// - 失败:返回状态码500
///
/// # 示例
///
/// ‍‍```
/// POST /users
/// Content-Type: application/json
///
/// {
///     "username": "johndoe",
///     "birthday": "1990-01-01T00:00:00",
///     "sex": "M",
///     "address": "123 Main St, Anytown, USA"
/// }
/// ‍‍```
#[utoipa::path(
    post,
    path = "/api/users",
    request_body = CreateUser,
    responses(
        (status = 200, description = "User created successfully", body = Model),
        (status = 500, description = "Internal server error")
    )
)]
pub async fn create_user(
    db: web::Data<sea_orm::DatabaseConnection>,
    user_data: web::Json<CreateUser>,
) -> impl Responder {
    let user = UserActiveModel {
        username: Set(user_data.username.clone()),
        birthday: Set(user_data.birthday),
        sex: Set(user_data.sex.clone()),
        address: Set(user_data.address.clone()),
        ..Default::default()
    };

    let result = user.insert(db.get_ref()).await;

    match result {
        Ok(user) => HttpResponse::Ok().json(user),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}
/// 获取指定ID的用户信息
///
/// # 路径参数
///
/// - `id`: 整数,用户ID
///
/// # 响应
///
/// - 成功:返回状态码200和用户JSON对象
/// - 未找到:返回状态码404
/// - 失败:返回状态码500
///
/// # 示例
///
/// ‍‍```
/// GET /users/1
/// ‍‍```
#[utoipa::path(
    get,
    path = "/api/users/{id}",
    responses(
        (status = 200, description = "User found", body = Model),
        (status = 404, description = "User not found"),
        (status = 500, description = "Internal server error")
    ),
    params(
        ("id" = i32, Path, description = "User ID")
    )
)]
pub async fn get_user(
    db: web::Data<sea_orm::DatabaseConnection>,
    id: web::Path<i32>,
) -> impl Responder {
    let user = user::Entity::find_by_id(*id).one(db.get_ref()).await;
    println!("{id}");
    match user {
        Ok(Some(user)) => HttpResponse::Ok().json(user),
        Ok(None) => HttpResponse::NotFound().finish(),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}
/// 更新指定ID的用户信息
///
/// # 路径参数
///
/// - `id`: 整数,用户ID
///
/// # 请求体
///
/// 需要一个JSON对象,包含以下字段(所有字段都是可选的):
/// - `username`: 字符串,新的用户名
/// - `birthday`: ISO 8601格式的日期时间字符串,新的用户生日
/// - `sex`: 字符串,新的用户性别
/// - `address`: 字符串,新的用户地址
///
/// # 响应
///
/// - 成功:返回状态码200和更新后的用户JSON对象
/// - 未找到:返回状态码404
/// - 失败:返回状态码500
///
/// # 示例
///
/// ‍‍```
/// PUT /users/1
/// Content-Type: application/json
///
/// {
///     "username": "johndoe_updated",
///     "address": "456 Elm St, Newtown, USA"
/// }
/// ‍‍```
#[utoipa::path(
    put,
    path = "/api/users/{id}",
    request_body = CreateUser,
    responses(
        (status = 200, description = "User updated successfully", body = Model),
        (status = 404, description = "User not found"),
        (status = 500, description = "Internal server error")
    ),
    params(
        ("id" = i32, Path, description = "User ID")
    )
)]
pub async fn update_user(
    db: web::Data<sea_orm::DatabaseConnection>,
    id: web::Path<i32>,
    user_data: web::Json<CreateUser>,
) -> impl Responder {
    let user = user::Entity::find_by_id(*id).one(db.get_ref()).await;

    match user {
        Ok(Some(user)) => {
            let mut user: UserActiveModel = user.into();
            user.username = Set(user_data.username.clone());
            user.birthday = Set(user_data.birthday);
            user.sex = Set(user_data.sex.clone());
            user.address = Set(user_data.address.clone());

            let result = user.update(db.get_ref()).await;

            match result {
                Ok(updated_user) => HttpResponse::Ok().json(updated_user),
                Err(_) => HttpResponse::InternalServerError().finish(),
            }
        }
        Ok(None) => HttpResponse::NotFound().finish(),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}
/// 删除指定ID的用户
///
/// # 路径参数
///
/// - `id`: 整数,用户ID
///
/// # 响应
///
/// - 成功:返回状态码204(无内容)
/// - 失败:返回状态码500
///
/// # 示例
///
/// ‍‍```
/// DELETE /users/1
/// ‍‍```
#[utoipa::path(
    delete,
    path = "/api/users/{id}",
    responses(
        (status = 204, description = "User deleted successfully"),
        (status = 500, description = "Internal server error")
    ),
    params(
        ("id" = i32, Path, description = "User ID")
    )
)]
pub async fn delete_user(
    db: web::Data<sea_orm::DatabaseConnection>,
    id: web::Path<i32>,
) -> impl Responder {
    let result = user::Entity::delete_by_id(*id).exec(db.get_ref()).await;

    match result {
        Ok(_) => HttpResponse::NoContent().finish(),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

为了使用Swagger UI查看接口文档,还需要创建src/api_doc.rs文件

#[derive(OpenApi)]
#[openapi(
    paths(
        handlers::user::create_user,
        handlers::user::get_user,
        handlers::user::update_user,
        handlers::user::delete_user
    ),
    components(
        schemas(Model,CreateUser)
    ),
    tags(
        (name = "users", description = "User management API")
    )
)]
pub struct ApiDoc;

在src/main.rs文件定义路由和配置Swagger UI

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let db: DatabaseConnection = db::establish_connection().await;
    let db_data = web::Data::new(db);

    HttpServer::new(move || {
        App::new()
            .app_data(db_data.clone())
            .service(
                web::scope("/api")
                    .service(
                        web::scope("/users")
                            .route("", web::post().to(create_user))
                            .route("/{id}", web::get().to(get_user))
                            .route("/{id}", web::put().to(update_user))
                            .route("/{id}", web::delete().to(delete_user))
                            .route("/test", web::get().to(|| async { "Hello, World!" }))
                    )
            )
            .service(
                SwaggerUi::new("/swagger-ui/{_:.*}")
                    .url("/api-docs/openapi.json", ApiDoc::openapi())
            )
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

到这里,项目完成开发,启动项目

cargo run

查看Swagger UI接口文档

浏览器打开http://localhost:8080/swagger-ui/

可以看到我们在Rust代码文件中的注释说明,这对于接口使用人员和代码维护人员都非常友好,当然对于接口的简单测试,在这个页面也是非常方便去进行

查看Rust标准文档

cargo doc --open

最后

项目的完整代码可以查看我的仓库​:https://github.com/VinciYan/rusty_crab_api.git

后续,我还会介绍如何使用Rust语言Web开发框架Salvo和SeaORM结合开发WebAPI

标签:WebAPI,web,Actix,description,db,pub,user,id
From: https://www.cnblogs.com/vinciyan/p/18412896

相关文章

  • 面试- Web安全
    XSS攻击(跨站脚本攻击)XSS预防<<>>XSRF(CSRF)攻击(跨站请求伪造)就像是你在不知情的情况下,被别人利用你的权限发起了某个你没打算进行的请求。重点是可以把你的用户信息给带过去,你不知不觉就帮我付款了。XSS是恶意代码“潜伏”在页面上,欺骗你去执行它,比如......
  • Web安全之HTTPS调用详解和证书说明案例示范
    随着互联网的高速发展,网络安全成为了一个不可忽视的话题,特别是在涉及用户敏感信息的业务系统中。在此背景下,使用HTTPS取代HTTP成为了大势所趋。本文将以电商交易系统为例,详细介绍HTTPS的重要性,并探讨如何通过HTTPS来提升网站的安全性。第一章HTTPS的必要性问题:HTTP的不足......
  • Web安全入门到精通(完全0基础)持续更新
    Web安全大纲目录Web安全大纲前言一、Web安全基础1.计算机网络基础前言       亲爱的小伙伴,很高兴在这个CSDN平台上与你相遇,在这里,兔兔我呀为大家准备一个简单易懂的Web安全教程,让即使是零基础的朋友也可以轻松掌握喔。       之所以写这......
  • EmbeddedBrowserWebView.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个EmbeddedBrowserWebView.dll文件(挑选合适的......
  • 2025年JavaWeb技术打造社区老人健康管理系统,3步轻松设计与实现,建议收藏!
    ✍✍计算机毕业编程指导师**⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!⚡⚡Java、Python、微信小程序、大数据实战项目集⚡⚡文末......
  • web群集--nginx实现重定向与重写操作的详细配置过程详与案例展示
    文章目录前言什么是重定向?重定向能做什么?何时需要重定向功能?nginx通过什么来实现重定向和重写操作的?nginx的重定向和重写有什么区别?案例展示重定向1.将所有对将所有对http://test.com的访问重定向到http://www.test.com重写将所有对http://test.com/old-path的访问......
  • Java Script - Web Api
    变量声明有3个ver、let和const。建议const优先,其次为let。constarr=['red','pink']arr.push('blue')arr=[1,2,4]arr.push(5)//错误,arr为const1、WebApi基本认知1.1、作用和分类作用:就是使用j......
  • WebSocket1
    服务端开启websocket中间件//需要UseWebSockets,否则无法使用WebSocketapp.UseWebSockets(newWebSocketOptions(){KeepAliveInterval=TimeSpan.FromSeconds(60),});//处理websocket的中间件app.UseMiddleware<WebsocketMiddlware>();WebsocketMiddlwarepublic......
  • 面试-JS Web API - 存储
    cookieHTML5存储(localStorage和sessionStorage)cookiecookie本身用于浏览器和server通讯的,被借用到本地存储来。可以用document.cookie来修改。同一个变量会覆盖,不同变量会追加。localStorage和sessionStorage//保存数据到localStoragelocalStorage.getItem('a......
  • 面试-JS Web API-Linux命令
    关键Linux命令虽然前端开发者不需要掌握Linux的所有命令,但以下基本的命令对日常工作是非常有用的:文件和目录管理ls:列出当前目录下的文件和文件夹。lsls-l#显示详细信息ls-a#显示隐藏文件cd:切换目录。cd/path/to/directorycd..#返回上一级目录pwd:显......