Rust

2025年07月15日更新 7 人订阅
原价: ¥ 6 限时优惠
专栏简介 Rust编程语言之错误处理 Rust 语言之 flod Rust编程语言之Cargo、Crates.io详解 Rust编程语言之枚举与模式匹配 Rust语言 - 接口设计的建议之受约束(Constrained) Rust编程语言之无畏并发 Rust语言 - 接口设计的建议之灵活(flexible) Rust语言 - 接口设计的建议之显而易见(Obvious) Rust语言 - 接口设计的建议之不意外(unsurprising) Rust 实战:构建实用的 CLI 工具 HTTPie Rust编程语言学习之高级特性 Rust内存管理揭秘:深度剖析指针与智能指针 解决Rust中数组和切片的编译时大小问题 《Rust编程之道》学习笔记一 Rust Async 异步编程 简易教程 使用 Async Rust 构建简单的 P2P 节点 Rust编程语言入门之模式匹配 Rust async 编程 Rust编程语言之编写自动化测试 Rust编程语言之函数式语言特性:迭代器和闭包 《Rust编程之道》学习笔记二 Rust Tips 比较数值 使用 Rust 开发一个微型游戏 Rust编程初探:深入理解Struct结构体 深入理解Rust中的内存管理:栈、堆与静态内存详解 深入理解 Rust 结构体:经典结构体、元组结构体和单元结构体的实现 深入掌握 Rust 结构体:从模板到实例化的完整指南 深入理解Rust中的结构体:逻辑与数据结合的实战示例 深入理解 Rust 枚举:从基础到实践 掌握Rust字符串的精髓:String与&str的最佳实践 全面解析 Rust 模块系统:实战案例与应用技巧 Rust 中的 HashMap 实战指南:理解与优化技巧 掌握Rust模式匹配:从基础语法到实际应用 Rust 中的面向对象编程:特性与实现指南 深入理解 Rust 的 Pin 和 Unpin:理论与实践解析 Rust Trait 与 Go Interface:从设计到实战的深度对比 从零开始:用 Rust 和 Axum 打造高效 Web 应用 Rust 错误处理详解:掌握 anyhow、thiserror 和 snafu Rust 如何优雅实现冒泡排序 链表倒数 K 节点怎么删?Python/Go/Rust 实战 用 Rust 玩转数据存储:JSON 文件持久化实战 Rust实战:打造高效字符串分割函数 如何高效学习一门技术:从知到行的飞轮效应 Rust 编程入门:Struct 让代码更优雅 Rust 编程:零基础入门高性能开发 用 Rust 写个猜数游戏,编程小白也能上手! Rust 入门教程:变量到数据类型,轻松掌握! 深入浅出 Rust:函数、控制流与所有权核心特性解析 从零开始:用 Rust 和 Axum 打造高效 Web 服务 Rust 集合类型解析:Vector、String、HashMap 深入浅出Rust:泛型、Trait与生命周期的硬核指南 Rust实战:博物馆门票限流系统设计与实现 用 Rust 打造高性能图片处理服务器:从零开始实现类似 Thumbor 的功能 Rust 编程入门实战:从零开始抓取网页并转换为 Markdown 深入浅出 Rust:高效处理二进制数据的 Bytes 与 BytesMut 实战 Rust智能指针:解锁内存管理的进阶之道 用 Rust 打造命令行利器:从零到一实现 mini-grep 解锁Rust代码组织:轻松掌握Package、Crate与Module Rust 所有权:从内存管理到生产力释放 深入解析 Rust 的面向对象编程:特性、实现与设计模式 Rust + Protobuf:从零打造高效键值存储项目 bacon 点燃 Rust:比 cargo-watch 更爽的开发体验 用 Rust 打造微型游戏:从零开始的 Flappy Dragon 开发之旅 函数式编程的Rust之旅:闭包与迭代器的深入解析与实践 探索Rust编程之道:从设计哲学到内存安全的学习笔记 精读《Rust编程之道》:吃透语言精要,彻底搞懂所有权与借用 Rust 避坑指南:搞定数值比较,别再让 0.1 + 0.2 != 0.3 困扰你! 告别 Vec!掌握 Rust bytes 库,解锁零拷贝的真正威力 告别竞态条件:基于 Axum 和 Serde 的 Rust 并发状态管理最佳实践 Rust 异步编程实践:从 Tokio 基础到阻塞任务处理模式 Rust 网络编程实战:用 Tokio 手写一个迷你 TCP 反向代理 (minginx) 保姆级教程:Zsh + Oh My Zsh 终极配置,让你的 Ubuntu 终端效率倍增 不止于后端:Rust 在 Web 开发中的崛起之路 (2024数据解读) Rust核心利器:枚举(Enum)与模式匹配(Match),告别空指针,写出优雅健壮的代码 Rust 错误处理终极指南:从 panic! 到 Result 的优雅之道 想用 Rust 开发游戏?这份超详细的入门教程请收好! 用 Rust 实现 HTTPie:一个现代 CLI 工具的构建过程 Rust 异步实战:从0到1,用 Tokio 打造一个高性能并发聊天室 深入 Rust 核心:彻底搞懂指针、引用与智能指针

从零开始:用 Rust 和 Axum 打造高效 Web 应用

从零开始:用Rust和Axum打造高效Web应用Rust以其高性能和安全性席卷开发圈,而Axum作为一款专注于人机工程学和模块化的Web框架,正成为Rust生态中的明星工具。想快速上手一个现代Web应用框本文详细介绍了如何使用Rust的Axum框架搭建We

从零开始:用 Rust 和 Axum 打造高效 Web 应用

Rust 以其高性能和安全性席卷开发圈,而 Axum 作为一款专注于人机工程学和模块化的 Web 框架,正成为 Rust 生态中的明星工具。想快速上手一个现代 Web 应用框

本文详细介绍了如何使用 Rust 的 Axum 框架搭建 Web 应用程序。从项目初始化、依赖配置,到实现基本的路由、API 端点和 JWT 认证功能,每一步都配有清晰的代码示例和运行说明。无论是返回简单的 “Hello, World!”,还是处理 JSON 数据及用户登录验证,你都能在这里找到实用指南。文章还结合了 jsonwebtoken 的集成,展示了如何为你的 Web 服务添加安全认证能力,适合 Rust 初学者和进阶开发者参考。

Axum 实操

创建项目并用VSCode 打开

~ via 🅒 base
➜ cd Code/rust

~/Code/rust via 🅒 base
➜ cargo new --lib axum-live
     Created library `axum-live` package

~/Code/rust via 🅒 base
➜ cd axum-live

axum-live on  master [?] via 🦀 1.72.0 via 🅒 base
➜ c

添加相关依赖并创建示例文件

axum-live on  master [?] via 🦀 1.72.0 via 🅒 base 
➜ cargo add axum           

axum-live on  master [?] is 📦 0.1.0 via 🦀 1.72.0 via 🅒 base took 2.6s 
➜ cargo add anyhow

axum-live on  master [?] is 📦 0.1.0 via 🦀 1.72.0 via 🅒 base 
➜ cargo add tokio --features full

axum-live on  master [?] is 📦 0.1.0 via 🦀 1.72.0 via 🅒 base 
➜ mkdir examples

axum-live on  master [?] is 📦 0.1.0 via 🦀 1.72.0 via 🅒 base 
➜ touch examples/basic.rs    

axum-live on  master [?] is 📦 0.1.0 via 🦀 1.72.0 via 🅒 base 
➜ cargo add serde --features derive
axum-live on  master [?] is 📦 0.1.0 via 🦀 1.72.0 via 🅒 base took 3.8s 
➜ cargo add jsonwebtoken      

examples/basic.rs

use std::net::SocketAddr;

use axum::{http::StatusCode, response::Html, routing::get, Json, Router, Server};
use serde::{Deserialize, Serialize};

// 定义一个 Todo 结构体
#[derive(Serialize, Deserialize, Debug)]
pub struct Todo {
    pub id: usize,
    pub title: String,
    pub completed: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct CreateTodo {
    pub title: String,
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(index_handler))
        .route("/todos", get(todos_handler).post(create_todo_handler));

    let addr = SocketAddr::from(([127, 0, 0, 1], 8000));
    println!("listening on http://{}", addr);

    Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn index_handler() -> Html<&'static str> {
    Html("<h1>Hello, World!</h1>")
}

async fn todos_handler() -> Json<Vec<Todo>> {
    Json(vec![
        Todo {
            id: 1,
            title: "Buy milk".to_string(),
            completed: false,
        },
        Todo {
            id: 2,
            title: "Buy eggs".to_string(),
            completed: false,
        },
    ])
}

async fn create_todo_handler(Json(todo): Json<CreateTodo>) -> StatusCode {
    println!("{:?}", todo);
    StatusCode::CREATED
}

Cargo.toml

[package]
name = "axum-live"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.75"
axum = "0.6.20"
jsonwebtoken = "8.3.0"
serde = { version = "1.0.188", features = ["derive"] }
tokio = { version = "1.32.0", features = ["full"] }

运行

axum-live on  master [?] is 📦 0.1.0 via 🦀 1.72.0 via 🅒 base 
➜ cargo run --example basic        
listening on http://127.0.0.1:8000

basic.http

http://localhost:8000/

### 
// todos_handler
GET http://localhost:8000/todos

###
POST http://localhost:8000/todos HTTP/1.1
content-type: application/json

{
    "title": "larry"
}

jsonwebtoken

<https://docs.rs/jsonwebtoken/latest/jsonwebtoken/>

<https://github.com/tokio-rs/axum/blob/main/examples/jwt/src/main.rs>

examples/basic.rs

use std::{net::SocketAddr, time::SystemTime};

use axum::{
    async_trait,
    extract::{FromRequest, FromRequestParts},
    headers::{authorization::Bearer, Authorization},
    http::{self, Request},
    http::{request::Parts, StatusCode},
    response::{Html, IntoResponse, Response},
    routing::{get, post},
    Json, RequestPartsExt, Router, Server, TypedHeader,
};
use jsonwebtoken as jwt;
use jwt::Validation;
use serde::{Deserialize, Serialize};
use serde_json::json;

const SECRET: &[u8] = b"deadbeef";

// 定义一个 Todo 结构体
#[derive(Serialize, Deserialize, Debug)]
pub struct Todo {
    pub id: usize,
    pub title: String,
    pub completed: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct CreateTodo {
    pub title: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct LoginRequest {
    email: String,
    password: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct LoginResponse {
    token: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    id: usize,
    name: String,
    exp: usize,
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(index_handler))
        .route("/todos", get(todos_handler).post(create_todo_handler))
        .route("/login", post(login_handler));

    let addr = SocketAddr::from(([127, 0, 0, 1], 8000));
    println!("listening on http://{}", addr);

    Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn index_handler() -> Html&lt;&'static str> {
    Html("&lt;h1>Hello, World!&lt;/h1>")
}

async fn todos_handler() -> Json&lt;Vec&lt;Todo>> {
    Json(vec![
        Todo {
            id: 1,
            title: "Buy milk".to_string(),
            completed: false,
        },
        Todo {
            id: 2,
            title: "Buy eggs".to_string(),
            completed: false,
        },
    ])
}

// "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6IlhpYW8gUWlhbyJ9.7PdpzWjZLN4KKNLoM07nfnhKnYdrc0IjumKcOUREXzI"
async fn create_todo_handler(claims: Claims, Json(todo): Json&lt;CreateTodo>) -> StatusCode {
    println!("{:?}", claims);
    println!("{:?}", todo);
    StatusCode::CREATED
}

async fn login_handler(Json(login): Json&lt;LoginRequest>) -> Json&lt;LoginResponse> {
    // skip login info validation
    println!("{:?}", login);

    let claims = Claims {
        id: 1,
        name: "Xiao Qiao".to_string(),
        exp: get_epoch() + 14 * 24 * 60 * 60,
    };
    let key = jwt::EncodingKey::from_secret(SECRET);
    let token = jwt::encode(&jwt::Header::default(), &claims, &key).unwrap();
    Json(LoginResponse { token })
}

// #[async_trait]
// impl&lt;S, B> FromRequest&lt;S, B> for Claims
// where
//     // these bounds are required by `async_trait`
//     B: Send + 'static,
//     S: Send + Sync,
//     // {
//     //     type Rejection = http::StatusCode;

//     //     async fn from_request(req: Request&lt;B>, state: &S) -> Result&lt;Self, Self::Rejection> {
//     //         // ...
//     //         let TypedHeader(Authorization(bearer)) =
//     //             TypedHeader::&lt;Authorization&lt;Bearer>>::from_request(req, state)
//     //                 .await
//     //                 .map_err(|_| http::StatusCode::NETWORK_AUTHENTICATION_REQUIRED)?;

//     //         let key = jwt::DecodingKey::from_secret(SECRET);
//     //         let claims = jwt::decode::&lt;Claims>(bearer.token(), &key, &jwt::Validation::default())
//     //             .map_err(|_| http::StatusCode::UNAUTHORIZED)?;
//     //         Ok(claims.claims)
//     //     }
//     // }
// {
//     type Rejection = HttpError;

//     async fn from_request(req: Request&lt;B>, state: &S) -> Result&lt;Self, Self::Rejection> {
//         // ...
//         let TypedHeader(Authorization(bearer)) =
//             TypedHeader::&lt;Authorization&lt;Bearer>>::from_request(req, state)
//                 .await
//                 .map_err(|_| HttpError::Auth)?;

//         let key = jwt::DecodingKey::from_secret(SECRET);
//         let token = jwt::decode::&lt;Claims>(bearer.token(), &key, &Validation::default())
//             .map_err(|_| HttpError::Auth)?;
//         Ok(token.claims)
//     }
// }

// #[derive(Debug)]
// enum HttpError {
//     Auth,
//     Internal,
//     NotFound,
//     InternalServerError,
// }

// impl IntoResponse for HttpError {
//     fn into_response(self) -> axum::response::Response {
//         let (code, msg) = match self {
//             HttpError::Auth => (StatusCode::UNAUTHORIZED, "Unauthorized"),
//             HttpError::NotFound => (StatusCode::NOT_FOUND, "Not Found"),
//             HttpError::InternalServerError => {
//                 (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error")
//             }
//             HttpError::Internal => (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error"),
//         };
//         (code, msg).into_response()
//     }
// }

fn get_epoch() -> usize {
    SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs() as usize
}

#[async_trait]
impl&lt;S> FromRequestParts&lt;S> for Claims
where
    S: Send + Sync,
{
    type Rejection = AuthError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result&lt;Self, Self::Rejection> {
        // Extract the token from the authorization header
        let TypedHeader(Authorization(bearer)) = parts
            .extract::&lt;TypedHeader&lt;Authorization&lt;Bearer>>>()
            .await
            .map_err(|_| AuthError::InvalidToken)?;
        let key = jwt::DecodingKey::from_secret(SECRET);
        // Decode the user data
        let token_data = jwt::decode::&lt;Claims>(bearer.token(), &key, &Validation::default())
            .map_err(|_| AuthError::InvalidToken)?;

        Ok(token_data.claims)
    }
}

impl IntoResponse for AuthError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            // AuthError::WrongCredentials => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
            // AuthError::MissingCredentials => (StatusCode::BAD_REQUEST, "Missing credentials"),
            // AuthError::TokenCreation => (StatusCode::INTERNAL_SERVER_ERROR, "Token creation error"),
            AuthError::InvalidToken => (StatusCode::BAD_REQUEST, "Invalid token"),
        };
        let body = Json(json!({
            "error": error_message,
        }));
        (status, body).into_response()
    }
}

#[derive(Debug)]
enum AuthError {
    // WrongCredentials,
    // MissingCredentials,
    // TokenCreation,
    InvalidToken,
}

basic.http

GET http://localhost:8000/ HTTP/1.1

### 
// todos_handler
GET http://localhost:8000/todos HTTP/1.1

###
POST http://localhost:8000/todos HTTP/1.1
content-type: application/json

{
    "title": "larry"
}

###
POST http://localhost:8000/login HTTP/1.1
content-type: application/json

{
    "email": "xiaoqiao@gmail.com",
    "password": "123456"
}

###
POST http://localhost:8000/todos HTTP/1.1
content-type: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6IlhpYW8gUWlhbyIsImV4cCI6MTY5NDk0NDQzNH0.FhUtNuyUtCA-gtMckc3UkvTu3Z2Ek8DYH1lg8PNXDmk

{
    "title": "hello world"
}

响应

HTTP/1.1 201 Created
content-length: 0
date: Sun, 03 Sep 2023 15:58:29 GMT

总结

通过本文的实战演练,我们从创建 Rust 项目开始,一步步探索了 Axum 框架的强大功能:简单的路由设计、灵活的请求处理,以及与 jsonwebtoken 结合实现的认证机制。Axum 不仅继承了 Rust 的性能优势,还以模块化和易用性降低了 Web 开发的门槛。无论你是 Rust 新手还是想提升 Web 开发技能的开发者,Axum 都值得一试。现在就动手实践,开启你的 Rust Web 开发之旅吧!

参考

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论