<div align="center">
<p><img alt="Savlo" width="132" style="max-width:40%;min-width:60px;" src="https://salvo.rs/images/logo-text.svg" /></p>
<p>
<a href="https://github.com/salvo-rs/salvo/blob/main/README.md">English</a>
<a href="https://github.com/salvo-rs/salvo/blob/main/README.zh-hans.md">ç®ä½ä¸æ</a>
<a href="https://github.com/salvo-rs/salvo/blob/main/README.zh-hant.md">ç¹é«ä¸æ</a>
</p>
<p>
<a href="https://github.com/salvo-rs/salvo/actions">
<img alt="build status" src="https://github.com/salvo-rs/salvo/workflows/ci-linux/badge.svg?branch=main&event=push" />
</a>
<a href="https://github.com/salvo-rs/salvo/actions">
<img alt="build status" src="https://github.com/salvo-rs/salvo/workflows/ci-macos/badge.svg?branch=main&event=push" />
</a>
<a href="https://github.com/salvo-rs/salvo/actions">
<img alt="build status" src="https://github.com/salvo-rs/salvo/workflows/ci-windows/badge.svg?branch=main&event=push" />
</a>
<br>
<a href="https://discord.gg/G8KfmS6ByH">
<img src="https://img.shields.io/discord/1041442427006890014.svg?logo=discord">
</a>
<a href="https://crates.io/crates/salvo"><img alt="crates.io" src="https://img.shields.io/crates/v/salvo" /></a>
<a href="https://docs.rs/salvo"><img alt="Documentation" src="https://docs.rs/salvo/badge.svg" /></a>
<a href="https://github.com/rust-secure-code/safety-dance/"><img alt="unsafe forbidden" src="https://img.shields.io/badge/unsafe-forbidden-success.svg" /></a>
<a href="https://blog.rust-lang.org/2024/07/25/Rust-1.80.0.html"><img alt="Rust Version" src="https://img.shields.io/badge/rust-1.80%2B-blue" /></a>
<br>
<a href="https://salvo.rs">
<img alt="Website" src="https://img.shields.io/badge/https-salvo.rs-%23f00" />
</a>
<a href="https://codecov.io/gh/salvo-rs/salvo"><img alt="codecov" src="https://codecov.io/gh/salvo-rs/salvo/branch/main/graph/badge.svg" /></a>
<a href="https://crates.io/crates/salvo"><img alt="Download" src="https://img.shields.io/crates/d/salvo.svg" /></a>
<img alt="License" src="https://img.shields.io/crates/l/salvo.svg" />
</p>
</div>
Salvo is an extremely simple and powerful Rust web backend framework. Only basic Rust knowledge is required to develop backend services.
## ð¯ Features
- Built with [Hyper 1](https://crates.io/crates/hyper) and [Tokio](https://crates.io/crates/tokio);
- HTTP1, HTTP2 and **HTTP3**;
- Unified middleware and handle interface;
- Router can be nested infinitely, and multiple middlewares can be attached to any router;
- Integrated Multipart form processing;
- Support WebSocket, WebTransport;
- Support OpenAPI, generate OpenAPI data automatic;
- Support Acme, automatically get TLS certificate from [let's encrypt](https://letsencrypt.org/);
- Support Tower Service and Layer;
## â¡ï¸ Quick Start
You can view samples [here](https://github.com/salvo-rs/salvo/tree/main/examples), or view [official website](https://salvo.rs).
### Hello World with ACME and HTTP3
**It only takes a few lines of code to implement a server that supports ACME to automatically obtain certificates and supports HTTP1, HTTP2, and HTTP3 protocols.**
```rust
use salvo::prelude::*;
#[handler]
async fn hello(res: &mut Response) {
res.render(Text::Plain("Hello World"));
}
#[tokio::main]
async fn main() {
let mut router = Router::new().get(hello);
let listener = TcpListener::new("0.0.0.0:443")
.acme()
.add_domain("test.salvo.rs") // Replace this domain name with your own.
.http01_challege(&mut router).quinn("0.0.0.0:443");
let acceptor = listener.join(TcpListener::new("0.0.0.0:80")).bind().await;
Server::new(acceptor).serve(router).await;
}
```
### Middleware
There is no difference between Handler and Middleware, Middleware is just Handler. **So you can write middlewares without to know concepts like associated type, generic type. You can write middleware if you can write function!!!**
```rust
use salvo::http::header::{self, HeaderValue};
use salvo::prelude::*;
#[handler]
async fn add_header(res: &mut Response) {
res.headers_mut()
.insert(header::SERVER, HeaderValue::from_static("Salvo"));
}
```
Then add it to router:
```rust
Router::new().hoop(add_header).get(hello)
```
This is a very simple middleware, it adds `Header` to `Response`, view [full source code](https://github.com/salvo-rs/salvo/blob/main/examples/middleware-add-header/src/main.rs).
### Chainable tree routing system
Normally we write routing like thisï¼
```rust
Router::with_path("articles").get(list_articles).post(create_article);
Router::with_path("articles/<id>")
.get(show_article)
.patch(edit_article)
.delete(delete_article);
```
Often viewing articles and article lists does not require user login, but creating, editing, deleting articles, etc. require user login authentication permissions. The tree-like routing system in Salvo can meet this demand. We can write routers without user login together:
```rust
Router::with_path("articles")
.get(list_articles)
.push(Router::with_path("<id>").get(show_article));
```
Then write the routers that require the user to login together, and use the corresponding middleware to verify whether the user is logged in:
```rust
Router::with_path("articles")
.hoop(auth_check)
.push(Router::with_path("<id>").patch(edit_article).delete(delete_article));
```
Although these two routes have the same `path("articles")`, they can still be added to the same parent route at the same time, so the final route looks like this:
```rust
Router::new()
.push(
Router::with_path("articles")
.get(list_articles)
.push(Router::with_path("<id>").get(show_article)),
)
.push(
Router::with_path("articles")
.hoop(auth_check)
.push(Router::with_path("<id>").patch(edit_article).delete(delete_article)),
);
```
`<id>` matches a fragment in the path, under normal circumstances, the article `id` is just a number, which we can use regular expressions to restrict `id` matching rules, `r"<id:/\d+/>"`.
You can also use `<**>`, `<*+>` or `<*?>` to match all remaining path fragments. In order to make the code more readable, you can also add appropriate name to make the path semantics more clear, for example: `<**file_path>`.
Some regular expressions for matching paths need to be used frequently, and it can be registered in advance, such as GUID:
```rust
PathFilter::register_wisp_regex(
"guid",
Regex::new("[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}").unwrap(),
);
```
This makes it more concise when path matching is required:
```rust
Router::with_path("<id:guid>").get(index)
```
View [full source code](https://github.com/salvo-rs/salvo/blob/main/examples/routing-guid/src/main.rs)
### File upload
We can get file async by the function `file` in `Request`:
```rust
#[handler]
async fn upload(req: &mut Request, res: &mut Response) {
let file = req.file("file").await;
if let Some(file) = file {
let dest = format!("temp/{}", file.name().unwrap_or_else(|| "file".into()));
if let Err(e) = tokio::fs::copy(&file.path, Path::new(&dest)).await {
res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
} else {
res.render("Ok");
}
} else {
res.status_code(StatusCode::BAD_REQUEST);
}
}
```
### Extract data from request
You can easily get data from multiple different data sources and assemble it into the type you want. You can define a custom type first, for example:
```rust
#[derive(Serialize, Deserialize, Extractible, Debug)]
/// Get the data field value from the body by default.
#[salvo(extract(default_source(from = "body")))]
struct GoodMan<'a> {
/// The id number is obtained from the request pa
baidu_16992441
- 粉丝: 311
- 资源: 1041
最新资源
- 《济南的冬天》教学设计与反思.docx
- 基于java+springboot+vue+mysql的古典舞在线交流平台 源码+数据库+论文(高分毕业设计).zip
- 形状检测32-YOLO(v5至v9)、COCO、CreateML、Darknet、Paligemma数据集合集.rar
- 百度智能云千帆大模型平台推进企业多模态生成式AI应用
- 互联网金融发展指数 (第二期,2014年1月-2015年12月).zip
- 社区团购网站:技术驱动下的电子商务新模式
- 2025年人形机器人产业发展蓝皮书-量产及商业化关键挑战
- C# 面试题 100 问:从基础到进阶,全面解析与实战.docx
- 基于java+springboot+vue+mysql的读书笔记共享平台 源码+数据库+论文(高分毕业设计).zip
- Python+Django+Mysql个性化图书推荐系统 图书在线推荐系统 基于用户、项目、内容的协同过滤推荐算法 帮远程安装部署 一、项目简介 1、开发工具和实现技术 Python3.8,Djan
- 基于Java的环境保护与宣传网站的设计与实现毕业论文.doc
- 基于java+springboot+vue+mysql的海滨体育馆管理系统 源码+数据库+论文(高分毕业设计).zip
- 2025年 UiPath AI和自动化趋势:代理型AI的崛起及企业影响
- 基于java+springboot+vue+mysql的网上超市系统 源码+数据库+论文(高分毕业设计).zip
- 电力系统静态稳定性仿真simulink仿真 用simulink搭建搭建单机无穷大系统,对其静态稳定性进行仿真分析
- 柑橘多种疾病类型图像分类数据集【已标注,约1,000张数据】
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈