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

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

系列文章目录

第一章 axum学习使用

文章目录

  • 系列文章目录
  • 前言
    • 老规矩先看官方文档介绍
    • 高级功能
    • 兼容性
  • 二、hello world
  • 三、路由
  • 四,handler和提取器
  • 五,响应


前言

本职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() {// 构建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里面的方法

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()

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

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

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

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())
}

相关文章:

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

系列文章目录 第一章 axum学习使用 文章目录 系列文章目录前言老规矩先看官方文档介绍高级功能兼容性 二、hello world三、路由四&#xff0c;handler和提取器五&#xff0c;响应 前言 本职java开发&#xff0c;兼架构设计。空闲时间学习了rust&#xff0c;目前还不熟练掌握。…...

oracle警告日志\跟踪日志磁盘空间清理

oracle警告日志\跟踪日志磁盘空间清理 问题现象&#xff1a; 通过查看排查到alert和tarce占用大量磁盘空间 警告日志 /u01/app/oracle/diag/rdbms/orcl/orcl/alert 跟踪日志 /u01/app/oracle/diag/rdbms/orcl/orcl/trace 解决方案&#xff1a; 用adrci清除日志 确定目…...

【vue】el-table 数据更新后,刷新表格数据

表格里面的数据更新后&#xff0c;可以通过以下方法来刷新表格 方法1 用更新后的数据&#xff0c;覆盖之前的数据 var newTableData[];for(var i0;i<that.tableData.length;i){ if(aIdthat.selectStationId&&bIdthat.selectDeviceId){that.tableData[i].physica…...

AVL——平衡搜索树

✅<1>主页&#xff1a;我的代码爱吃辣&#x1f4c3;<2>知识讲解&#xff1a;数据结构——AVL树☂️<3>开发环境&#xff1a;Visual Studio 2022&#x1f4ac;<4>前言&#xff1a;AVL树是对二叉搜索树的严格高度控制&#xff0c;所以AVL树的搜索效率很高…...

TCP通信流程以及一些TCP的相关概念

1.TCP和UDP区别 都为传输层协议 UDP&#xff1a;用户数据报协议&#xff0c;面向无连接&#xff0c;可以单播&#xff0c;多播&#xff0c;广播&#xff0c;面向数据报&#xff0c;不可靠 TCP&#xff1a;传输控制协议&#xff0c;面向连接的&#xff0c;可靠的&#xff0c;基…...

PyTorch学习笔记(十七)——完整的模型验证(测试,demo)套路

完整代码&#xff1a; import torch import torchvision from PIL import Image from torch import nnimage_path "../imgs/dog.png" image Image.open(image_path) print(image)# 因为png格式是四个通道&#xff0c;除了RGB三通道外&#xff0c;还有一个透明度通…...

WPF开篇

一、为什么要学习WPF 大环境不好&#xff0c;公司要求逐年提高&#xff0c;既要会后端又要会客户端WPF相对于WinForm来说用户界面效果更好&#xff0c;图像更加立体化也是给自己增加一项技能&#xff0c;谨记一句话&#xff0c;技多不压身&#xff1b;多一份技能就多一份竞争力…...

linux 压缩解压缩

压缩解压缩 linux中压缩和解压文件也是很常见的 zip格式 zip格式的压缩包在windows很常见&#xff0c;linux中也有zip格式的压缩包 #压缩#zip [选项] 压缩包名 文件(多个文件空格隔开)zip 1.zip 123.txt 456.txt zip -r 2.zip /home/user1 ---------------------- -r 压缩目录 …...

centos9 mysql8修改数据库的存储路径

一、环境 系统&#xff1a;CentOS Stream release 9 mysql版本&#xff1a;mysql Ver 8.0.34 for Linux on x86_64 (MySQL Community Server - GPL) 二、修改mysql的数据库&#xff0c;存储路径 查看目录数据存储的位置 cat /etc/my.cnf操作 1、新建存放的目录&#xff0c;…...

【C++】<Windows编程中消息即事件的处理>

目录 一、注册窗口类&#xff0c;指定消息处理函数&#xff0c;捕获消息并发给处理函数 二、消息处理函数 三、通用窗口消息 四、其他消息 1.滚动条消息 2.按钮控件消息 3.按钮控件通知消息 4.按键消息 5.系统菜单等消息 6.组合框控件消息 7.组合框控件通知消息 8.列…...

数据库SQL语句使用

-- 查询所有数据库 show databases; -- 创建数据库&#xff0c;数据库名为mydatabase create database mydatabase; -- 如果没有名为 mydatabase的数据库则创建&#xff0c;有就不创建 create database if not exists mydatabase; -- 如果没有名为 mydatabase的数据库则创建…...

从零开始 Spring Cloud 12:Sentinel

从零开始 Spring Cloud 12&#xff1a;Sentinel 1.初识 Sentinel 1.1雪崩问题 1.1.1什么是雪崩问题 微服务中&#xff0c;服务间调用关系错综复杂&#xff0c;一个微服务往往依赖于多个其它微服务。 如图&#xff0c;如果服务提供者I发生了故障&#xff0c;当前的应用的部分…...

@Resurce和@Autowired的区别

Resource 和 Autowired 是 Java 中常用的两个注解&#xff0c;用于自动装配依赖对象。它们的主要区别如下&#xff1a; 来源不同&#xff1a; Resource 是 Java EE 提供的注解&#xff0c;属于 J2EE 的一部分&#xff0c;它由 JSR-250 规范定义。 Autowired 是 Spring 框架提供…...

ResNet简介

ResNet (Residual Network) 此网络于2015年&#xff0c;国人何先生提出&#xff0c;用于解决随着深度学习的层数加深造成的网络退化现象和梯度消失、梯度爆炸。 问题1 退化现象 当深度学习的各项指标能够随着训练轮数收敛的情况下&#xff0c;网络的层数增强未能像理论一样&…...

了解单例模式,工厂模式(简单易懂)

文章目录 单例模式饿汉模式懒汉模式对比 工厂模式简单工厂模式&#xff08;Simple Factory Pattern&#xff09;工厂方法模式&#xff08;Factory Method Pattern&#xff09;抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;对比 单例模式 什么是单例&#xff…...

【中危】 Apache NiFi 连接 URL 验证绕过漏洞 (CVE-2023-40037)

漏洞描述 Apache NiFi 是一个开源的数据流处理和自动化工具。 在受影响版本中&#xff0c;由于多个Processors和Controller Services在配置JDBC和JNDI JMS连接时对URL参数过滤不完全。使用startsWith方法过滤用户输入URL&#xff0c;导致过滤可以被绕过。攻击者可以通过构造特…...

【Git版本控制工具使用---讲解一】

Git版本控制工具使用 安装设置用户名签名和邮箱Git常用的命令 初始化本地库查看本地状态Git 命令添加暂存区提交本地库查看版本信息修改文件版本穿梭 安装 首先根据自身电脑的配置选择性的安装是32位的还是64位的Git版本控制工具 我这边安装的是64位的 以下是我安装的时候的过…...

NLP | 基于LLMs的文本分类任务

比赛链接&#xff1a;讯飞开放平台 来源&#xff1a;DataWhale AI夏令营3&#xff08;NLP&#xff09; Roberta-base&#xff08;BERT的改进&#xff09; ①Roberta在预训练的阶段中没有对下一句话进行预测&#xff08;NSP&#xff09; ②采用了动态掩码 ③使用字符级和词级…...

攻防世界-base÷4

原题 解题思路 base644&#xff0c;莫不是base16&#xff0c;base16解码网站&#xff1a; 千千秀字...

【Java转Go】快速上手学习笔记(三)之基础篇二

【Java转Go】快速上手学习笔记&#xff08;二&#xff09;之基础篇一 了解了基本语法、基本数据类型这些使用&#xff0c;接下来我们来讲数组、切片、值传递、引用传递、指针类型、函数、map、结构体。 目录 数组和切片值传递、引用传递指针类型defer延迟执行函数map结构体匿名…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...

自然语言处理——文本分类

文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益&#xff08;IG&#xff09; 分类器设计贝叶斯理论&#xff1a;线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别&#xff0c; 有单标签多类别文本分类和多…...