1、Cargo.toml
[dependencies] # Axum Web框架,用于构建HTTP服务 axum = "0.8" # Tokio异步运行时,Axum的基础依赖 tokio = { version = "1.50", features = ["full"] } # 数据库操作 sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] } # 序列化和反序列化 serde = { version = "1.0", features = ["derive"] }2、error.rs(错误处理)
useaxum::http::StatusCode;useaxum::response::IntoResponse;// 定义我们的错误枚举pubenumAppError{// 数据库操作失败DatabaseError(sqlx::Error),// 资源未找到,建议添加上下文(NotFound(String))记录缺失资源名,便于日志追踪NotFound,}// 实现 IntoResponse 让错误可响应// 这样 Axum 就能自动把我们的错误转成 HTTP 响应implIntoResponseforAppError{fninto_response(self)->axum::response::Response{let(status,msg)=matchself{AppError::DatabaseError(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"数据库出错,请稍后重试!"),AppError::NotFound=>(StatusCode::NOT_FOUND,"数据不存在!"),};// 直接转为响应,自动设置 Content-Type: text/plain; charset=utf-8(status,msg).into_response()}}// 实现从 sqlx::Error 自动转到 AppError (可以使用 “?” 操作符)// 可以实现其他错误类型的转换,做到错误处理统一implFrom<sqlx::Error>forAppError{fnfrom(err:sqlx::Error)->Self{AppError::DatabaseError(err)}}StatusCode:Axum 提供的 HTTP 状态码枚举,如StatusCode::NOT_FOUND、StatusCode::INTERNAL_SERVER_ERROR,用于标准化响应状态。IntoResponse:Axum 的核心 trait,任何实现了它的类型都可以直接作为路由的返回值,Axum 会自动将其转换为 HTTP 响应。这是你自定义错误能“被框架识别”的关键。
3、models.rs(数据模型)
useserde::{Deserialize,Serialize};// 数据库实体#[derive(Serialize, sqlx::FromRow)]pubstructTodo{pubid:i64,pubtitle:String,// 待办事项pubcompleted:bool,// 是否弯沉}// 新增待办事项使用#[derive(Deserialize)]pubstructCreateTodo{pubtitle:String,// 待办事项}// 全局状态,数据库连接池pubstructAppState{pubdb:sqlx::SqlitePool,}4、method_routers.rs(路由方法)
usecrate::error::AppError;usecrate::models::{AppState,CreateTodo,Todo};useaxum::Json;useaxum::extract::{Path,State};useaxum::http::StatusCode;usesqlx::SqlitePool;usestd::sync::Arc;// 初始化数据库连接,会在当前目录生成 todo.dbpubasyncfninit_db()->Arc<AppState>{// 创建数据库连接,读写模式,数据库不存在时创建letpool=SqlitePool::connect("sqlite:todo.db?mode=rwc").await.unwrap();// 创建表(如果不存在)sqlx::query("CREATE TABLE IF NOT EXISTS todos ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, completed BOOL NOT NULL DEFAULT 0)",).execute(&pool).await.unwrap();Arc::new(AppState{db:pool})}// 添加待办pubasyncfncreate_todo(State(state):State<Arc<AppState>>,Json(payload):Json<CreateTodo>,)->Result<(StatusCode,Json<Todo>),AppError>{letid=sqlx::query("INSERT INTO todos (title,completed) VALUES (?,0)").bind(&payload.title).execute(&state.db).await?.last_insert_rowid();// 返回创建记录的idletnew_todo=Todo{id,title:payload.title,completed:false,};Ok((StatusCode::CREATED,Json(new_todo)))}// 查询所有待办事项pubasyncfnlist_todos(State(state):State<Arc<AppState>>,)->Result<(StatusCode,Json<Vec<Todo>>),AppError>{lettodos=sqlx::query_as("SELECT id,title,completed FROM todos").fetch_all(&state.db)// 查询全部数据.await?;Ok((StatusCode::OK,Json(todos)))}// 完成一项待办事项pubasyncfncomplete_todo(State(state):State<Arc<AppState>>,Path(id):Path<i64>,)->Result<StatusCode,AppError>{letresult=sqlx::query("UPDATE todos SET completed = 1 WHERE id = ?").bind(id).execute(&state.db).await?;// 修改数据影响的数据条数,0时为未修改,返回数据不存在ifresult.rows_affected()==0{returnErr(AppError::NotFound);}Ok(StatusCode::OK)}// 删除一条待办事项pubasyncfndelete_todo(State(state):State<Arc<AppState>>,Path(id):Path<i64>,)->Result<StatusCode,AppError>{letresult=sqlx::query("DELETE FROM todos WHERE id = ?").bind(id).execute(&state.db).await?;// 删除数据影响的数据条数,0时为未删除,返回数据不存在ifresult.rows_affected()==0{returnErr(AppError::NotFound);}Ok(StatusCode::OK)}SQLite数据库打开模式:
mode=ro:只读模式。mode=rw:读写模式。mode=rwc:读写模式,数据库不存在时创建。mode=memory:内存数据库,只在程序运行期间存在。
5、main.rs(主文件)
modmethod_routers;moderror;modmodels;usecrate::method_routers::{complete_todo,create_todo,delete_todo,init_db,list_todos};useaxum::routing::{get,put};useaxum::{response::IntoResponse,Router};usetokio::net::TcpListener;#[tokio::main]asyncfnmain(){// 初始化数据库连接letshared_state=init_db().await;// 创建路由letapp=Router::new().route("/todos",get(list_todos).post(create_todo))// 查询,增加.route("/todos/{id}",put(complete_todo).delete(delete_todo))// 修改,删除.with_state(shared_state);// 将数据库连接,绑定到路由的全局状态// 绑定端口letlistener=TcpListener::bind("0.0.0.0:8080").await.unwrap();println!("添加 post请求: http://127.0.0.1:8080/todos");println!(" post请求体:{{\"title\":\"我的待办事项一\"}}");println!("查询所有待办事项 get请求: http://127.0.0.1:8080/todos");println!("修改 put请求: http://127.0.0.1:8080/todos/修改的id");println!("删除 delete请求: http://127.0.0.1:8080/todos/删除的id");// 启动服务axum::serve(listener,app).await.unwrap();}Path路径写法:
Axum 0.6及以下写法,/users/:id**
Axum 0.7+**写法,/users/{id}