当前位置: 首页 > news >正文

Axum web框架【实习】

概念:

定义:Axum是Rust生态的一个现代web框架,基于Tokio、Tower、Hyper这些库。

Tokio(异步运行时)、Tower(模块化中间件系统)、Hyper(HTTP实现)

特点:异步支持、类型安全、中间件系统,最重要的是类型安全,Rust 给予编译时检查减少运行时错误。

核心功能:

  1. 异步优先:完全基于async/await语法、充分利用Rust的异步生态(Tokio)。
  2. 类型安全:通过编译期间的类型检查错误(路由参数不匹配、请求数据格式错误),减少运行时的问题。
  3. 模块中间件:没有自己的中间件系统,是基于Tower中间件系统,轻松集成日志、CORS、认证、限流等,且支持自定义中间件。
  4. 提取器(Extractors):可以灵活地从请求中提取数据进行声明式解析(路径参数、查询参数、请求头、请求体等),且支持自定义提取器。
  5. 路由简洁:直观的语法定义路由,支持嵌套路由、通配符,容易维护。
  6. Rust生态兼容:无缝集成序列化和反序列化(Serde)、数据库交互(sqlx)等主流库。

用途:通过组合组件构建web应用,构建REST API、微服务以及其他HTTP服务。

一个Hello World的入门示例:

[dependencies]
axum = "0.6.16"
tokio = {version = "1.0", features = ["full"]} 
//full意味着开启tokio库所有可选功能

添加上面的依赖项,就可以编码了

use axum::{//定义HTTP GET请求的路由处理函数routing::get,//构建路由表,将传入的路径映射到相应的处理函数Router,
};
//网络套接字地址,指定服务器监听的ip和port
use std::net::SocketAddr;
//宏,将主函数转换成异步函数,使用异步运行时
#[tokio::main]
async fn main() {//创建一个Router实例,将跟路径"/"的请求映射给root函数let app = Router::new().route("/", get(root));//创建套接字实例,绑定到指定的ip和portlet addr = SocketAddr::from(([127, 0, 0, 1], 3000));//启动HTTP服务器,监听指定的地址和端口,并处理传入的请求axum::Server::bind(&addr).serve(app.into_make_service()).await      //异步等待服务器启动.unwrap();  //处理可能出现的错误
}
//异步处理函数,当客户端访问跟路径时,返回静态字符串
async fn root() -> &'static str {"Hello, World!"
}

使用命令cargo run启动后,浏览器跑一下或像下图另开终端输入命令:curl -X GET http://127.0.0.1:3000,可以看到hello,world!的输出

路由(Routers)

Router设置了哪些路径指向哪些服务

use axum::routing::{get, post};
use axum::{Router, Server};
//宏,将主函数转换成异步函数,使用异步运行时
#[tokio::main]
async fn main() {//创建路由实例,进行路由配置:路径+方法对应的处理函数let app = Router::new().route("/", get(root))                      //http://127.0.0.1:3000/.route("/foo", get(get_foo).post(post_foo)) //http://127.0.0.1:3000/foo.route("/foo/bar", get(get_foo_bar));       //http://127.0.0.1:3000/foo/bar//启动HTTP服务器,监听指定的地址和端口,并处理传入的请求axum::Server::bind(&"127.0.0.1:3000".parse().unwrap()).serve(app.into_make_service()).await      //异步等待服务器启动.unwrap();  //处理可能出现的错误
}
async fn root() -> String {String::from("Hello axum!")
}
async fn get_foo() -> String {String::from("get请求的foo")
}
async fn post_foo() -> String {String::from("post请求的foo")
}
async fn get_foo_bar() -> String {String::from("get请求的foo/bar")
}

cargo run 运行后,另开终端输入curl -X GET http://127.0.0.1/foo或curl -X POST http://127.0.0.1/foo进行试验

处理器(Handler)

在axum中,处理器就是一个异步函数或异步代码块,接收着提取器的产物作为参数,并返回一些可以转换成IntoResponse的内容。

处理器是应用程序逻辑存在的地方,而应用程序是通过处理器之间的路由构建的。

提取器(Extractor)

使用提取器可以把一个请求的数据提取出来,采用声明式解析,作用是解析出来的数据用于处理程序所需的部分(例如:解析异步函数的参数,请求的URL匹配,就会运行该异步函数)

use axum::extract::{Path, Query, Json};  
use std::collections::HashMap;  // Path路径,eg. /users/<id>  
async fn path(Path(user_id): Path<u32>) {}  
// Query参数,eg. /users?id=123&name=jim  
async fn query(Query(params): Query<HashMap<String, String>>) {}
// Json 格式参数,一般用于 POST 请求  
async fn json(Json(payload): Json<serde_json::Value>) {}

例如:Extrator中的Json就是一个提取器,异步处理函数接受请求后解析成JSON

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

添加如上依赖后,可以编写一个HTTP POST请求给服务器,服务器解析请求中的请求体,返回JSON反序列化后的Rust结构体。

use axum::{routing::post,http::StatusCode,Router,extract::Json,   //消耗主体并解析成JSON
};
use serde::{Deserialize,Serialize}; //将Json数据反序列化成Rust结构体#[derive(Deserialize,Serialize,Debug)]
struct CreateUser {username: String,   // 接收创建用户的请求名
}//参数:从请求中提取JSON数据反序列化成结构体
async fn create_user(Json(payload):Json<CreateUser>) ->(StatusCode,Json<CreateUser>){//响应内容是Json格式(StatusCode::CREATED,Json(payload))
}#[tokio::main]
async fn main(){let app =Router::new().route("/users",post(create_user));axum::Server::bind(&"127.0.0.1:3000".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
}

在终端上cargo run启动后,执行命令curl -H "Content-Type: application/json" -d '{"username":"TXS"}' -X POST 或 浏览器输入URL:http://127.0.0.1:3000/users

注意:浏览器默认发送的都是GET请求,所以在浏览器输入URL:http://127.0.0.1:3000/users 不会显现POST请求的页面,因为此代码也没有发送一个get请求,光可以发送post请求。

axum 提供了许多有用的提取器,例如:

  • BytesStringBody, 和 BodyStream 用于获取请求正文
  • MethodHeaderMap, 和 Uri 用于获取请求的特定部分
  • FormQueryUrlParams, 和 UrlParamsMap 用于更高级别的请求解析
  • Extension 用于跨处理程序共享状态的扩展
  • Request<hyper::Body> 如果你想完全控制
  • Result<T, E> and Option<T> 使提取器成为可选

你也可以通过实现 FromRequest 来定义你自己的提取器。

构建响应(IntoResponse)

处理器返回的任何类型的值,只要是有关实现了IntoResponse的值,Axum就可以自动把这些值转换成HTTP响应。

意味着在实践中,很少需要建立响应,或许也可以实现IntoResponse创建自己的特定响应

//引入的依赖
use http::StatusCode;
use axum::response::{Html, Json};   //创建HTML响应,创建JSON响应
use serde_json::{json, Value};//返回静态的字符串会自动转换成状态码200的响应
async fn text() -> &'static str {"Hello, World!"
}//同理
async fn string() -> String {"Hello, World!".to_string()
}//返回一个元组,也会转换成一个状态码为404的响应
async fn not_found() -> (StatusCode, &'static str) {(StatusCode::NOT_FOUND, "not found")
}//返回的HTML,转换成200响应
async fn html() -> Html<&'static str> {Html("<h1>Hello, World!</h1>")
}//返回JSON对象,也会返回200响应
async fn json() -> Json<Value> {Json(json!({ "data": 42 }))
}

错误处理(Error handing)

auxm提供了一个错误处理模型,那么错误转换成响应就非常简单了,并且保证所有错误都可以得到处理。

use std::time::Duration;use axum::{body::Body,error_handling::{HandleError, HandleErrorLayer},http::{Method, Response, StatusCode, Uri},response::IntoResponse,routing::get,BoxError, Router,
};
use tower::ServiceBuilder;#[tokio::main]
async fn main() {let app = Router::new().merge(router_fallible_service()) // 模拟使用 Service的错误处理.merge(router_fallible_middleware()) // 模拟使用中间件的错误处理.merge(router_fallible_extractor());  // 模拟使用提取器的错误处理  let addr = "127.0.0.1:3000";println!("listening on {}", addr);axum::Server::bind(&addr.parse().unwrap()).serve(app.into_make_service()).await.unwrap();
}// 错误处理方式1: 模拟使用 Service的错误处理
fn router_fallible_service() -> Router {// 这个 Service 可能出现任何错误let some_fallible_service = tower::service_fn(|_req| async {thing_that_might_fail().await?;Ok::<_, anyhow::Error>(Response::new(Body::empty()))});Router::new().route_service("/",// Service 适配器通过将错误转换为响应来处理错误。HandleError::new(some_fallible_service, handle_anyhow_error),)
}// 业务处理逻辑,可能出现失败而抛出 Error
async fn thing_that_might_fail() -> Result<(), anyhow::Error> {// 模拟一个错误anyhow::bail!("thing_that_might_fail")
}// 把错误转化为 IntoResponse
async fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) {(StatusCode::INTERNAL_SERVER_ERROR,format!("Something went wrong: {}", err),)
}// 处理器:模拟超时
async fn handler_timeout() -> impl IntoResponse {println!("sleep 3 seconds");tokio::time::sleep(Duration::from_secs(3)).await; // 休眠3秒,模拟超时format!("Hello Error Handling !!!")
}// 错误处理方式2 : 用中间件处理错误的路由
fn router_fallible_middleware() -> Router {Router::new().route("/fallible_middleware", get(handler_timeout)).layer(ServiceBuilder::new()// `timeout` will produce an error if the handler takes// too long so we must handle those.layer(HandleErrorLayer::new(handler_timeout_error)).timeout(Duration::from_secs(1)),)
}// 用中间件处理错误
async fn handler_timeout_error(err: BoxError) -> (StatusCode, String) {if err.is::<tower::timeout::error::Elapsed>() {(StatusCode::REQUEST_TIMEOUT,"Request time too long, Timeout!!!".to_string(),)} else {(StatusCode::INTERNAL_SERVER_ERROR,format!("Unhandled internal error: {}", err),)}
}// 错误处理方式3: 用运行时提取器处理错误的路由
fn router_fallible_extractor() -> Router {Router::new().route("/fallible_extractor", get(handler_timeout)).layer(ServiceBuilder::new()// `timeout` will produce an error if the handler takes// too long so we must handle those.layer(HandleErrorLayer::new(handler_timeout_fallible_extractor)).timeout(Duration::from_secs(1)),)
}// 用运行时提取器处理错误
async fn handler_timeout_fallible_extractor(// `Method` and `Uri` are extractors so they can be used heremethod: Method,uri: Uri,// the last argument must be the error itselferr: BoxError,
) -> (StatusCode, String) {(StatusCode::INTERNAL_SERVER_ERROR,format!("`{} {}` failed with {}", method, uri, err),)
}

文章转载自:

http://IXSttcKv.mxxsq.cn
http://rirsh4Zn.mxxsq.cn
http://FHx2KMza.mxxsq.cn
http://wHK6pzPO.mxxsq.cn
http://7YBvTfkf.mxxsq.cn
http://40OllOQQ.mxxsq.cn
http://OhYZ58FR.mxxsq.cn
http://yKKV4bns.mxxsq.cn
http://CJCkpaX6.mxxsq.cn
http://cBrik2Sw.mxxsq.cn
http://2QWT6gNY.mxxsq.cn
http://7BRkXNXJ.mxxsq.cn
http://RjDted65.mxxsq.cn
http://6AMCDFGp.mxxsq.cn
http://VLpyo9ve.mxxsq.cn
http://1dEIAzVL.mxxsq.cn
http://CMBGpkj6.mxxsq.cn
http://W7zWIHif.mxxsq.cn
http://QLBQ9u0Q.mxxsq.cn
http://L65Dasc4.mxxsq.cn
http://OIbH963l.mxxsq.cn
http://m2ENjaYh.mxxsq.cn
http://VIEuMubO.mxxsq.cn
http://3EiXOjKx.mxxsq.cn
http://i1oFL292.mxxsq.cn
http://aOjYGnWw.mxxsq.cn
http://KdUYxkLA.mxxsq.cn
http://lIcF1uV4.mxxsq.cn
http://5iFAL3c0.mxxsq.cn
http://6xsMGzua.mxxsq.cn
http://www.dtcms.com/a/374600.html

相关文章:

  • 吾律——让普惠法律服务走进生活
  • 【重学 MySQL】一百、MySQL的权限管理与访问控制
  • STM32F103C8T6开发板入门学习——点亮LED灯2
  • RISC-V体系架构
  • 创作纪念日·512天
  • 【芯片设计-信号完整性 SI 学习 1.1 -- 眼图、抖动、反射、串扰】
  • 小迪安全v2023学习笔记(八十讲)—— 中间件安全WPS分析WeblogicJenkinsJettyCVE
  • 【Linux】基础指令(下)
  • linux 环境下Docker 安装
  • Nginx 配置
  • 20250910_《SQL Server 数据库事务日志定期清理方案(精简优化版)》以10.1.1.31服务器的gtp-default数据库为例
  • 多输入(input)多输出(output)验证
  • 排查JSch连接SFTP服务器失败的问题
  • JMeter压测过程中监控服务器CPU及内存的方法
  • 整理python快速构建数据可视化前端的Dash库
  • Redis缓存穿透、缓存击穿与雪崩防护及性能优化实战指南
  • ArcGIS学习-20 实战-地形研究
  • Ubuntu下基于Nginx+ffmpeg+video.js的HLS流媒体视频播放方案
  • Vue2 VS Vue3
  • 【ArcGIS】如何编辑图层的属性表
  • VueFlow的箭头怎么调整
  • 基于Vue3 +ElementuiPlus + Dexie.js自研的浏览器插件新建标签页tab
  • 【序列晋升】30 Spring Cloud Vault 安全配置管理的微服务守护者
  • 狂想-一种新颖的低成本内嵌标记的视触觉感知前导方案
  • 兰洋科技双展联动展示液冷创新成果,技术驱动打造绿色算力新基建
  • INDEMIND亮相2025科技创变者大会,以机器人空间智能技术解锁具身智能新边界
  • 百度SEM里什么是搜索广告、搜索词、否定关键词、上方位(竞价)广告?
  • 百度竞价推广:百度搜索竞价推广代运营
  • rabbitmq如何保证消息不丢失
  • 做百度SEM付费搜索推广时,竞价账号定向怎么设置?