Axum路由

main.rs

use axum::{
    routing::get,
    Router,
};
// 属性宏,将此函数标记为异步程序的入口点,启动一个异步运行时(Tokio 运行时)来执行这个异步函数
#[tokio::main]
async fn main() {
    // 创建路由实例
    let app = Router::new().route("/hello", get(|| async { "Hello, Axum!"}));
    // 使用hyper监听所有地址的9090端口,.await等待异步完成,绑定成功返回TcpListener实例,失败panic并打印错误信息
    let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await.unwrap();
    println!("->>LISTENING on {:?}",listener);
    // 使用serve函数启动一个异步服务器,监听TcpListener实例,并使用app作为处理函数
    axum::serve(listener, app).await.unwrap();
}

使用postman请求0.0.0.0:8080/hello可以看到返回了数据

Hello, Axum!

0.0.0.0表示所有ipv4地址,但不能被ping通

127.0.0.1表示回环地址。所有网络号为127的地址都是回环地址

Rust项目源代码发生变化时自动运行 Cargo 命令

# 安装(关闭杀毒软件)
cargo install cargo-watch

监听src/目录,更改代码自动重新执行cargo run

cargo watch -q -c -w src/ -x run

若要监听tests/目录,更改代码自动重新执行cargo test -q test_dev -- --nocapture测试并显示所有输出

cargo watch -q -c -w tests/ -x "test -q test_dev -- --nocapture"

axum::Router路由

闭包传递路由

闭包可以捕获调用者作用域中的值

use axum::{
    routing::get,
    Router,
    extract::Path,
};
// use tracing::info;
#[tokio::main]
async fn main() {

    let app = Router::new()
    .route("/", get(|| async { "Hello, Rust!" }))
    .route("/hello", get(|| async { "Hello, World!" }))
    .route("/tokio/:name", get(|name:Path<String>| async move{ format!("Hello,{:?}",name) }));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

还可以将路由函数提取出来,相同路由可以的不同处理可以通过.添加处理器并添加自定义方法,如.get().post().patch().delete()

use axum::{
    routing::get,
    Router,
};
use tokio::net::TcpListener;

// 获取
async fn get_handler() -> String {
    "Hello, world!".to_string()
}
// 创建
async fn post_handler() -> String {
    "post".to_string()
}
// 更新
async fn patch_handler() -> String {
    "update".to_string()
}
// 删除
async fn delete_handler() -> String {
    "delete".to_string()
}
#[tokio::main]
async fn main() {
    
    let app = Router::new()
       .route("/", get(get_handler))
       .route("/hello", get(get_handler).post(post_handler).patch(patch_handler).delete(delete_handler));

    let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

幂等:一个操作被多次重复执行多次,其结果与第一次执行的结果相同

同样的请求被执行一次与连续执行多次,对服务器的预期影响是相同的,那么称这个 HTTP 方法是幂等的,如PUTDELETE

所有的安全方法都是幂等的,如GETHEADOPTIONS

安全:一个 HTTP 方法是安全的,是指这是个方法不会修改服务器的数据,即只读的方法,如GETHEADOPTIONS

  • GET(获取资源)请求资源
    • GET 请求是安全、幂等的
  • POST(创建资源):通常用于向服务器提交数据以创建新的资源
    • POST 请求是不安全、不幂等的
    • POST 请求的主体可以包含任意格式的数据,例如表单数据、JSON 或 XML
  • PUT(更新资源):更新服务器上的现有资源,客户端将完整的资源表示发送到服务器,服务器用这个表示替换现有的资源
    • PUT请求是不安全、幂等的
  • PATCH(部分更新资源): PATCH 只需要提供资源的部分,服务器只更新指定的部分
    • PATCH请求是不安全、不幂等的
  • DELETE(删除资源):删除指定的资源
    • DELETE请求是不安全、幂等的
  • HEAD(获取资源头信息):只返回资源的头部信息,不返回资源的主体内容,用于检查资源的存在性、获取资源的大小、最后修改时间等信息,而不需要下载整个资源
  • OPTIONS(获取服务器支持的方法):获取服务器支持的 HTTP 方法和其他选项信息,客户端发送 OPTIONS 请求以了解服务器对特定资源的支持情况
    • OPTIONS请求的响应通常包含一个Allow头部,列出服务器支持的方法
    • OPTIONS 请求可以用于客户端在发送实际请求之前了解服务器的能力和限制

路由匹配

:创建动态路由,可作为传递的值,必须有值才能匹配到

  • /hello/:id匹配/hello/12
  • /:id/hello匹配/12/hello

*创建通配符路由

  • /hello/*file匹配/hello/sssssssfile
  • 特殊/hello/*key不匹配/hello/但会匹配/hello/下的所有路由,如/hello/cci/cci/cci/

多个参数传递使用axum::extract::Path提取

路径只包含一个参数时,可以省略元组

use axum::{
    extract::Path,
    routing::get,
    Router,
};
use tokio::net::TcpListener;

// 获取
async fn get_handler() -> String {
    "Hello, world!".to_string()
}
async fn show_user(Path((user_id,team_id)):Path<(String,String)>)-> String {
    format!("{}_{}", user_id, team_id)
}
#[tokio::main]
async fn main() {
    
    let app = Router::new()
       .route("/", get(get_handler))
       .route("/users/:user_id/team/:team_id", get(show_user));
        
    let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

nest 嵌套路由

将路由嵌套在另一个路由下,例如将用户相关的路由嵌套在users下,请求路径必须包含users

  • 嵌套路由不会看到原始请求 URI,而是会删除匹配的前缀
  • 使用原始URI请使用axum::extract::OriginalUri
  • 嵌套路由和通配符路由功能类似,嵌套路由会删除前缀,通配符路由保留完整路由
use axum::{
    extract::Path,
    routing::{get,post},
    Router,
};
use tokio::net::TcpListener;
async fn show_user(Path(id): Path<String>) -> String {
    format!("id: {:?}", id)
}
async fn post_user() -> String {
    "post_user".to_string()
}
#[tokio::main]
async fn main() {
    let user_routes = Router::new().route("/:id", get(show_user));
    let team_routes = Router::new().route("/", post(post_user));

    let api_routes = Router::new()
    .nest("/users", user_routes)// GET /api/users/145632
    .nest("/teams", team_routes);// POST /api/teams/
    let app = Router::new().nest("/api", api_routes);
        
    let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

fallback 后备路由

fallback:后备,备用方案或回退机制,主要的操作或功能无法正常执行时,程序可以使用预先定义的 fallback来处理情况,以确保程序不会完全失败或崩溃

后背路由仅适用于路由中任何内容均不匹配的路由,创建路由后使用.fallback()添加后备路由

callback:区分回调函数

如果嵌套路由没有自己的后备,那么将继承外部路由的后备,以下例子

  • 当请求:8080/api/users/145632时可以匹配成功
  • 当请求:8080/test时,由于没有路由可以匹配,会执行/api定义的后备
  • 当请求:8080/api/users/时,由于没有路由可以匹配,会执行/users定义的后备
  • 当请求:8080/api/users/145632/test时,由于没有路由可以匹配,/:id路由没有定义后备路由,会执行外部的/users定义的后备
use axum::{
    http::Uri,
    extract::OriginalUri,
    routing::get,
    Router,
    http::StatusCode
};
use tokio::net::TcpListener;
async fn show_user(uri: Uri, OriginalUri(original_uri): OriginalUri) -> String {
    format!("uri: {:?}\noriginal_uri: {:?}\n", uri,original_uri)
}
async fn fallback_api() -> (StatusCode, &'static str) {
    (StatusCode::NOT_FOUND, "Not Found /api")
}
async fn fallback_users() -> (StatusCode, &'static str) {
    (StatusCode::NOT_FOUND, "Not Found /users")
}

#[tokio::main]
async fn main() {
    let user_routes = Router::new().route("/:id", get(show_user));

    let api_routes = Router::new()
    .nest("/users", user_routes).fallback(fallback_users);

    let app = Router::new().nest("/api", api_routes).fallback(fallback_api);
        
    let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

axum::extract::OriginalUri

获取原始URI

use axum::{
    http::Uri,
    extract::OriginalUri,
    routing::{get,post},
    Router,
};
use tokio::net::TcpListener;
async fn show_user(uri: Uri, OriginalUri(original_uri): OriginalUri) -> String {
    //uri: /145632
    //original_uri: /api/users/145632
    format!("uri: {:?}\noriginal_uri: {:?}\n", uri,original_uri)
}
async fn post_user() -> String {
    "post_user".to_string()
}
#[tokio::main]
async fn main() {
    let user_routes = Router::new().route("/:id", get(show_user));
    let team_routes = Router::new().route("/", post(post_user));

    let api_routes = Router::new()
    .nest("/users", user_routes)// GET \api\users\145632
    .nest("/teams", team_routes);// POST \api\teams\
    let app = Router::new().nest("/api", api_routes);
        
    let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

merge 合并路由

将多个独立的路由组合到一起统一处理,例如两个模块中定义了路由,在主应用中合并为一个路由

use axum::{
    routing::get,
    Router,
    extract::Path,
};
use tokio::net::TcpListener;
async fn users_list() -> String {
    "users list".to_string()
}
async fn users_show(Path(id):Path<String>) -> String {
    format!("user show: {:?}",id)
}
async fn teams_list() -> String {
    "teams list".to_string()
}
#[tokio::main]
async fn main() {

    let user_routes = Router::new()
        .route("/users", get(users_list)) // GET :8080/users
        .route("/users/:id", get(users_show)); // GET :8080/users/145632

    let team_routes = Router::new()
        .route("/teams", get(teams_list)); // GET :8080/teams

    let app = Router::new()
        .merge(user_routes)
        .merge(team_routes);

    let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

路由还可以携带状态,如数据库连接、context,使用.with_state(state),案例见https://github.com/SeaQL/sea-orm/blob/master/examples/axum_example/api/src/lib.rs

以下是一个简单的示例,展示如何使用Rust后端框架Axum来创建一个基本的Web应用程序: 首先,确保您已经安装Rust和Cargo。然后,在您的项目目录中创建一个新的Cargo.toml文件,并添加以下内容: ```toml [package] name = "axum_example" version = "0.1.0" edition = "2021" [dependencies] axum = "0.2" tokio = { version = "1", features = ["full"] } ``` 接下来,创建一个main.rs文件,并添加以下代码: ```rust use axum::{handler::get, Router}; use std::net::SocketAddr; async fn hello_world() -> &'static str { "Hello, world!" } #[tokio::main] async fn main() { // 创建一个Axum应用程序的根路由 let app = Router::new().route("/", get(hello_world)); // 定义服务器的地址和端口 let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); // 运行服务器 axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } ``` 上述代码创建了一个简单的Axum应用程序,该应用程序定义了一个根路由"/",并使用GET方法处理请求。处理程序函数hello_world返回一个字符串"Hello, world!"作为响应。 最后,打开终端,导航到您的项目目录,并运行以下命令来构建和运行应用程序: ``` cargo build cargo run ``` 您应该会看到类似于以下输出: ``` Listening on http://127.0.0.1:3000 ``` 现在,您可以在浏览器中访问http://127.0.0.1:3000,并应该看到"Hello, world!"的响应。 这只是Axum的一个简单示例,您可以根据需要添加更多的路由和处理程序来构建更复杂的应用程序。Axum提供了许多功能和中间件,使您能够处理不同类型的请求和实现复杂的业务逻辑。您可以参考Axum的官方文档以获得更多详细信息和示例代码:https://docs.rs/axum/0.2.5/axum/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cci497

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值