0


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

系列文章目录

第一章 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

看看官方例子

useaxum::{routing::get,Router,};#[tokio::main]asyncfnmain(){// 构建routerlet app =Router::new().route("/",get(||async{"Hello, World!"}));// 运行hyper  http服务 localhost:3000axum::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!

三、路由

路由设置路径有哪些handler去处理
handler可以理解为springboot开发当中的controller里面的方法

useaxum::{Router,routing::get};// our routerlet app =Router::new().route("/",get(root))//路径对应handler.route("/foo",get(get_foo).post(post_foo)).route("/foo/bar",get(foo_bar));// 一个个handlerasyncfnroot(){}asyncfnget_foo(){}asyncfnpost_foo(){}asyncfnfoo_bar(){}

创建路由

Router::new()

说一些常用方法
nest方法可以嵌套一些别的路由

useaxum::{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

其实就大致相当于springboot当中在controller类上设置总路径。
merge方法将两个路由器合并为一个

useaxum::{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方法,对于不同的请求方式

useaxum::{Router,routing::{get, delete},extract::Path};let app =Router::new().route("/",get(get_root).post(post_root).delete(delete_root),);asyncfnget_root(){}asyncfnpost_root(){}asyncfndelete_root(){}

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

四,handler和提取器

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

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

useaxum::{extract::Json,routing::post,handler::Handler,Router,};useserde::Deserialize;#[derive(Deserialize)]structCreateUser{
    email:String,
    password:String,}asyncfncreate_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"

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

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

useaxum::{extract::{Path,Query},routing::get,Router,};useuuid::Uuid;useserde::Deserialize;let app =Router::new().route("/users/:id/things",get(get_user_things));#[derive(Deserialize)]structPagination{
    page:usize,
    per_page:usize,}implDefaultforPagination{fndefault()->Self{Self{ page:1, per_page:30}}}asyncfnget_user_things(Path(user_id):Path<Uuid>,
    pagination:Option<Query<Pagination>>,){letQuery(pagination)= pagination.unwrap_or_default();// ...}

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

useaxum::Json;useserde::Deserialize;#[derive(Deserialize)]structPayload{}asyncfnhandler(// 这种是不被允许的,body被处理了两次
    string_body:String,
    json_body:Json<Payload>,){// ...}

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

useaxum::{extract::Json,routing::post,Router,};useserde_json::Value;asyncfncreate_user(payload:Option<Json<Value>>){ifletSome(payload)= payload {}else{}}let app =Router::new().route("/users",post(create_user));

五,响应

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

useaxum::{Json,response::{Html,IntoResponse},http::{StatusCode,Uri,header::{self,HeaderMap,HeaderName}},};// 空的asyncfnempty(){}// 返回string,此时`text/plain; charset=utf-8` content-typeasyncfnplain_text(uri:Uri)->String{format!("Hi from {}", uri.path())}// 返回bytes`application/octet-stream` content-typeasyncfnbytes()->Vec<u8>{vec![1,2,3,4]}// 返回json格式asyncfnjson()->Json<Vec<String>>{Json(vec!["foo".to_owned(),"bar".to_owned()])}// 返回html网页格式`text/html` content-typeasyncfnhtml()->Html<&'staticstr>{Html("<p>Hello, World!</p>")}// 返回响应码,返回值空asyncfnstatus()->StatusCode{StatusCode::NOT_FOUND}// 返回值的响应头asyncfnheaders()->HeaderMap{letmut headers =HeaderMap::new();
    headers.insert(header::SERVER,"axum".parse().unwrap());
    headers
}// 数组元组设置响应头asyncfnarray_headers()->[(HeaderName,&'staticstr);2]{[(header::SERVER,"axum"),(header::CONTENT_TYPE,"text/plain")]}// 只要是实现IntoResponse 都可以返回asyncfnimpl_trait()->implIntoResponse{[(header::SERVER,"axum"),(header::CONTENT_TYPE,"text/plain")]}

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

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

useaxum::{http::{Response,StatusCode},into_response::IntoResponse,response::Html};// 创建一个自定义响应对象structMyResponse(String);// 创建一个impl块,实现`IntoResponse` traitimplIntoResponseforMyResponse{typeBody=Html<String>;typeError=std::convert::Infallible;fninto_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响应,并返回。

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

asyncfnmy_handler()->implIntoResponse{MyResponse("<h1>Hello, Axum!</h1>".to_string())}
标签: rust web 后端

本文转载自: https://blog.csdn.net/qq_35270805/article/details/132364165
版权归原作者 爱音乐的程序猿 所有, 如有侵权,请联系我们删除。

“【从零开始的rust web开发之路 一】axum学习使用”的评论:

还没有评论