【从零开始的rust web开发之路 一】axum学习使用

【从零开始的rust web开发之路 一】axum学习使用目前 rust 语言 web 开发相关的框架已经有很多 但还是和 java go 语言比不了

大家好,欢迎来到IT知识分享网。

系列文章目录

第一章 axum学习使用


前言

本职java开发,兼架构设计。空闲时间学习了rust,目前还不熟练掌握。想着用urst开发个web服务,正好熟悉一下rust语言开发。
目前rust 语言web开发相关的框架已经有很多,但还是和java,go语言比不了。
这个系列想完整走一遍web开发,后续有时间就出orm,还有一些别的web用到的库教程。
言归正传,开始学习axum框架

老规矩先看官方文档介绍

Axum是一个专注于人体工程学和模块化的Web应用程序框架。

高级功能

使用无宏 API 将请求路由到处理程序。
使用提取程序以声明方式分析请求。
简单且可预测的错误处理模型。
使用最少的样板生成响应。
充分利用塔和塔-http生态系统 中间件、服务和实用程序。
特别是,最后一点是与其他框架的区别。 没有自己的中间件系统,而是使用tower::Service。这意味着获得超时、跟踪、压缩、 授权等等,免费。它还使您能够与 使用 hyper 或 tonic 编写的应用程序。axumaxumaxum

兼容性

Axum旨在与Tokio和Hyper配合使用。运行时和 传输层独立性不是目标,至少目前是这样。

tokio框架在rust异步当中相当流行。axum能很好地搭配tokio实现异步web

二、hello world

看看官方例子

use axum::{ 
    routing::get, Router, }; #[tokio::main] async fn main() { 
    // 构建router let app = Router::new().route("/", get(|| async { 
    "Hello, World!" })); // 运行hyper http服务 localhost:3000 axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); } 

要想使用还需要引入库

[dependencies] axum = "0.6.19" tokio = { 
    version = "1.29.1", features = ["full"] } tower = "0.4.13" 

这时候就可以运行了,访问localhost:3000此时就能在页面看到Hello, World!

三、路由

use axum::{ 
   Router, routing::get}; // our router let app = Router::new() .route("/", get(root)) //路径对应handler .route("/foo", get(get_foo).post(post_foo)) .route("/foo/bar", get(foo_bar)); // 一个个handler async fn root() { 
   } async fn get_foo() { 
   } async fn post_foo() { 
   } async fn foo_bar() { 
   } 

创建路由

Router::new() 
use axum::{ 
    routing::{ 
   get, post}, Router, }; let user_routes = Router::new().route("/:id", get(|| async { 
   })); let team_routes = Router::new().route("/", post(|| async { 
   })); let api_routes = Router::new() .nest("/users", user_routes) .nest("/teams", team_routes); let app = Router::new().nest("/api", api_routes); //此时有两个路径 // - GET /api/users/:id // - POST /api/teams 
use axum::{ 
    routing::get, Router, }; // user路由 let user_routes = Router::new() .route("/users", get(users_list)) .route("/users/:id", get(users_show)); // team路由 let team_routes = Router::new() .route("/teams", get(teams_list)); // 合并 let app = Router::new() .merge(user_routes) .merge(team_routes); // 此时接受请求 // - GET /users // - GET /users/:id // - GET /teams 

router可以接受多个handler方法,对于不同的请求方式

use axum::{ 
   Router, routing::{ 
   get, delete}, extract::Path}; let app = Router::new().route( "/", get(get_root).post(post_root).delete(delete_root), ); async fn get_root() { 
   } async fn post_root() { 
   } async fn delete_root() { 
   } 

如果你之前用过go语言中的gin框架,那么上手这个会简单很多

四,handler和提取器

handler是一个异步函数,它接受零个或多个“提取器”作为参数并返回一些 可以转换为响应。
处理程序是应用程序逻辑所在的位置,也是构建 axum 应用程序的位置 通过在处理程序之间路由。
它采用任意数量的 “提取器”作为参数。提取器是实现 FromRequest 或 FromRequestPart 的类型

例如,Json 提取器,它使用请求正文和 将其反序列化为 JSON 为某种目标类型,可以用来解析json格式

use axum::{ 
    extract::Json, routing::post, handler::Handler, Router, }; use serde::Deserialize; #[derive(Deserialize)] struct CreateUser { 
    email: String, password: String, } async fn create_user(Json(payload): Json<CreateUser>) { 
    // 这里payload参数类型为CreateUser结构体,并且字段参数已经被赋值 } let app = Router::new().route("/users", post(create_user)); 

注意需要引入serde 依赖

serde = { 
    version = "1.0.176", features = ["derive"] } serde_json = "1.0.104" 

还有一些其他的常用的提取器,用于解析不同类型参数

use axum::{ 
    extract::{ 
   Json, TypedHeader, Path, Extension, Query}, routing::post, headers::UserAgent, http::{ 
   Request, header::HeaderMap}, body::{ 
   Bytes, Body}, Router, }; use serde_json::Value; use std::collections::HashMap; // `Path`用于解析路径上的参数,比如/path/:user_id,这时候请求路径/path/100,那么user_id的值就是100,类似springboot当中@PathVariable注解 async fn path(Path(user_id): Path<u32>) { 
   } // 查询路径请求参数值,这里转换成hashmap对象了,类似springboot当中@RequestParam注解 async fn query(Query(params): Query<HashMap<String, String>>) { 
   } // `HeaderMap`可以获取所有请求头的值 async fn headers(headers: HeaderMap) { 
   } //TypedHeader可以用于提取单个标头(header),请注意这需要您启用了axum的headers功能 async fn user_agent(TypedHeader(user_agent): TypedHeader<UserAgent>) { 
   } //获得请求体中的数据,按utf-8编码 async fn string(body: String) { 
   } //获得请求体中的数据,字节类型 async fn bytes(body: Bytes) { 
   } //这个使json类型转换成结构体,上面的例子讲了 async fn json(Json(payload): Json<Value>) { 
   } // 这里可以获取Request,可以自己去实现更多功能 async fn request(request: Request<Body>) { 
   } //Extension从"请求扩展"中提取数据。这里可以获得共享状态 async fn extension(Extension(state): Extension<State>) { 
   } //程序的共享状态,需要实现Clone #[derive(Clone)] struct State { 
    /* ... */ } let app = Router::new() .route("/path/:user_id", post(path)) .route("/query", post(query)) .route("/user_agent", post(user_agent)) .route("/headers", post(headers)) .route("/string", post(string)) .route("/bytes", post(bytes)) .route("/json", post(json)) .route("/request", post(request)) .route("/extension", post(extension)); 

每个handler参数可以使用多个提取器提取参数

use axum::{ 
    extract::{ 
   Path, Query}, routing::get, Router, }; use uuid::Uuid; use serde::Deserialize; let app = Router::new().route("/users/:id/things", get(get_user_things)); #[derive(Deserialize)] struct Pagination { 
    page: usize, per_page: usize, } impl Default for Pagination { 
    fn default() -> Self { 
    Self { 
    page: 1, per_page: 30 } } } async fn get_user_things( Path(user_id): Path<Uuid>, pagination: Option<Query<Pagination>>, ) { 
    let Query(pagination) = pagination.unwrap_or_default(); // ... } 

提取器的顺序
提取程序始终按函数参数的顺序运行,从左到右。
请求正文是只能使用一次的异步流。 因此,只能有一个使用请求正文的提取程序
例如

 use axum::Json; use serde::Deserialize; #[derive(Deserialize)] struct Payload { 
   } async fn handler( // 这种是不被允许的,body被处理了两次 string_body: String, json_body: Json<Payload>, ) { 
    // ... } 

那么如果参数是可选的需要这么多,使用Option包裹

use axum::{ 
    extract::Json, routing::post, Router, }; use serde_json::Value; async fn create_user(payload: Option<Json<Value>>) { 
    if let Some(payload) = payload { 
    } else { 
    } } let app = Router::new().route("/users", post(create_user)); 

五,响应

响应内容只要是实现 IntoResponse就能返回

use axum::{ 
    Json, response::{ 
   Html, IntoResponse}, http::{ 
   StatusCode, Uri, header::{ 
   self, HeaderMap, HeaderName}}, }; // 空的 async fn empty() { 
   } // 返回string,此时`text/plain; charset=utf-8` content-type async fn plain_text(uri: Uri) -> String { 
    format!("Hi from {}", uri.path()) } // 返回bytes`application/octet-stream` content-type async fn bytes() -> Vec<u8> { 
    vec![1, 2, 3, 4] } // 返回json格式 async fn json() -> Json<Vec<String>> { 
    Json(vec!["foo".to_owned(), "bar".to_owned()]) } // 返回html网页格式`text/html` content-type async fn html() -> Html<&'static str> { 
    Html("<p>Hello, World!</p>") } // 返回响应码,返回值空 async fn status() -> StatusCode { 
    StatusCode::NOT_FOUND } // 返回值的响应头 async fn headers() -> HeaderMap { 
    let mut headers = HeaderMap::new(); headers.insert(header::SERVER, "axum".parse().unwrap()); headers } // 数组元组设置响应头 async fn array_headers() -> [(HeaderName, &'static str); 2] { 
    [ (header::SERVER, "axum"), (header::CONTENT_TYPE, "text/plain") ] } // 只要是实现IntoResponse 都可以返回 async fn impl_trait() -> impl IntoResponse { 
    [ (header::SERVER, "axum"), (header::CONTENT_TYPE, "text/plain") ] } 

关于自定义IntoResponse,看看ai怎么说

要自定义实现IntoResponse,按照以下步骤进行:
创建一个实现http::Response的结构体,该结构体将承载您的自定义响应对象。
创建一个impl块,实现IntoResponse trait。
在into_response方法中,根据需要生成您的自定义响应。

use axum::{ 
   http::{ 
   Response, StatusCode}, into_response::IntoResponse, response::Html}; // 创建一个自定义响应对象 struct MyResponse(String); // 创建一个impl块,实现`IntoResponse` trait impl IntoResponse for MyResponse { 
    type Body = Html<String>; type Error = std::convert::Infallible; fn into_response(self) -> Response<Self::Body> { 
    // 根据需要生成您的自定义响应 Response::builder() .status(StatusCode::OK) .header("Content-Type", "text/html") .body(Html(self.0)) .unwrap() } } 

在上面的代码中,我们实现了一个名为MyResponse的自定义响应对象,并为其实现了IntoResponse trait。在into_response方法中,我们将自定义响应对象转换为一个HTML响应,并返回。

您可以像下面这样使用这个自定义响应对象:

async fn my_handler() -> impl IntoResponse { 
    MyResponse("<h1>Hello, Axum!</h1>".to_string()) } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/157070.html

(0)
上一篇 2025-02-08 16:26
下一篇 2025-02-08 16:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信