salvo-basic-app

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Salvo Basic Application Setup

Salvo基础应用搭建

This skill helps create basic Salvo web applications with proper structure and best practices.
本技能可帮助你遵循合理结构与最佳实践创建基础Salvo Web应用。

Core Concepts

核心概念

Handler

处理器(Handler)

Handlers process HTTP requests. Use the
#[handler]
macro on async functions:
rust
use salvo::prelude::*;

#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

#[handler]
async fn greet(req: &mut Request) -> String {
    let name = req.query::<String>("name").unwrap_or("World".to_string());
    format!("Hello, {}!", name)
}
Handler parameters can be in any order and are all optional:
  • req: &mut Request
    - HTTP request object
  • res: &mut Response
    - HTTP response object
  • depot: &mut Depot
    - Request-scoped data storage
  • ctrl: &mut FlowCtrl
    - Flow control for middleware
处理器用于处理HTTP请求。在异步函数上使用
#[handler]
宏:
rust
use salvo::prelude::*;

#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

#[handler]
async fn greet(req: &mut Request) -> String {
    let name = req.query::<String>("name").unwrap_or("World".to_string());
    format!("Hello, {}!", name)
}
处理器参数可以是任意顺序,且均为可选:
  • req: &mut Request
    - HTTP请求对象
  • res: &mut Response
    - HTTP响应对象
  • depot: &mut Depot
    - 请求作用域的数据存储
  • ctrl: &mut FlowCtrl
    - 中间件的流程控制

Router

路由(Router)

Routers define URL paths and attach handlers:
rust
use salvo::prelude::*;

let router = Router::new()
    .get(hello)
    .push(Router::with_path("greet").get(greet));
路由用于定义URL路径并关联处理器:
rust
use salvo::prelude::*;

let router = Router::new()
    .get(hello)
    .push(Router::with_path("greet").get(greet));

Server Setup

服务器配置

Basic server configuration:
rust
use salvo::prelude::*;

#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

#[tokio::main]
async fn main() {
    let router = Router::new().get(hello);
    let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
    Server::new(acceptor).serve(router).await;
}
基础服务器配置:
rust
use salvo::prelude::*;

#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

#[tokio::main]
async fn main() {
    let router = Router::new().get(hello);
    let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
    Server::new(acceptor).serve(router).await;
}

Response Types

响应类型

Returning Different Types

返回不同类型

Handlers can return any type implementing
Writer
or
Scribe
:
rust
use salvo::prelude::*;

#[handler]
async fn json_response() -> Json<serde_json::Value> {
    Json(serde_json::json!({"status": "ok"}))
}

#[handler]
async fn text_response() -> &'static str {
    "Plain text response"
}

#[handler]
async fn html_response(res: &mut Response) {
    res.render(salvo::writing::Html("<h1>Hello</h1>"));
}

#[handler]
async fn status_response() -> StatusCode {
    StatusCode::NO_CONTENT
}

#[handler]
async fn redirect_response(res: &mut Response) {
    res.render(salvo::writing::Redirect::found("https://example.com"));
}
处理器可以返回任何实现
Writer
Scribe
的类型:
rust
use salvo::prelude::*;

#[handler]
async fn json_response() -> Json<serde_json::Value> {
    Json(serde_json::json!({"status": "ok"}))
}

#[handler]
async fn text_response() -> &'static str {
    "Plain text response"
}

#[handler]
async fn html_response(res: &mut Response) {
    res.render(salvo::writing::Html("<h1>Hello</h1>"));
}

#[handler]
async fn status_response() -> StatusCode {
    StatusCode::NO_CONTENT
}

#[handler]
async fn redirect_response(res: &mut Response) {
    res.render(salvo::writing::Redirect::found("https://example.com"));
}

Rendering JSON

渲染JSON

rust
use salvo::prelude::*;
use serde::Serialize;

#[derive(Serialize)]
struct User {
    name: String,
    age: u8,
}

#[handler]
async fn get_user() -> Json<User> {
    Json(User {
        name: "Alice".to_string(),
        age: 30,
    })
}
rust
use salvo::prelude::*;
use serde::Serialize;

#[derive(Serialize)]
struct User {
    name: String,
    age: u8,
}

#[handler]
async fn get_user() -> Json<User> {
    Json(User {
        name: "Alice".to_string(),
        age: 30,
    })
}

Error Handling

错误处理

Return
Result<T, E>
where both implement
Writer
:
rust
use salvo::prelude::*;

#[handler]
async fn may_fail() -> Result<Json<Data>, StatusError> {
    let data = fetch_data().await.map_err(|_| StatusError::internal_server_error())?;
    Ok(Json(data))
}
返回
Result<T, E>
,其中T和E均需实现
Writer
rust
use salvo::prelude::*;

#[handler]
async fn may_fail() -> Result<Json<Data>, StatusError> {
    let data = fetch_data().await.map_err(|_| StatusError::internal_server_error())?;
    Ok(Json(data))
}

Request Object

请求对象

Common Request Methods

常用请求方法

rust
#[handler]
async fn handle_request(req: &mut Request) -> String {
    // Get request method
    let method = req.method();

    // Get request URI
    let uri = req.uri();

    // Get header value
    if let Some(content_type) = req.header::<String>("Content-Type") {
        println!("Content-Type: {}", content_type);
    }

    // Get query parameter
    let name = req.query::<String>("name").unwrap_or_default();

    // Get path parameter (requires route like /users/{id})
    let id = req.param::<i64>("id").unwrap();

    // Parse JSON body
    let body: UserData = req.parse_json().await.unwrap();

    format!("Processed request")
}
rust
#[handler]
async fn handle_request(req: &mut Request) -> String {
    // 获取请求方法
    let method = req.method();

    // 获取请求URI
    let uri = req.uri();

    // 获取头信息值
    if let Some(content_type) = req.header::<String>("Content-Type") {
        println!("Content-Type: {}", content_type);
    }

    // 获取查询参数
    let name = req.query::<String>("name").unwrap_or_default();

    // 获取路径参数(需要类似/users/{id}的路由)
    let id = req.param::<i64>("id").unwrap();

    // 解析JSON请求体
    let body: UserData = req.parse_json().await.unwrap();

    format!("Processed request")
}

Response Object

响应对象

Common Response Methods

常用响应方法

rust
use salvo::prelude::*;

#[handler]
async fn handle_response(res: &mut Response) {
    // Set status code
    res.status_code(StatusCode::CREATED);

    // Set response header
    res.headers_mut().insert("X-Custom-Header", "value".parse().unwrap());

    // Render text response
    res.render("Hello, World!");
}
rust
use salvo::prelude::*;

#[handler]
async fn handle_response(res: &mut Response) {
    // 设置状态码
    res.status_code(StatusCode::CREATED);

    // 设置响应头
    res.headers_mut().insert("X-Custom-Header", "value".parse().unwrap());

    // 渲染文本响应
    res.render("Hello, World!");
}

Dependencies

依赖项

Add to
Cargo.toml
:
toml
[dependencies]
salvo = "0.89.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
添加到
Cargo.toml
toml
[dependencies]
salvo = "0.89.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Best Practices

最佳实践

  1. Use
    #[handler]
    macro for all handlers
  2. Keep handlers focused on single responsibility
  3. Use appropriate return types (Json, Text, StatusCode)
  4. Handle errors with Result types
  5. Use
    TcpListener
    for basic HTTP servers
  6. Extract common logic into middleware using
    hoop()
  1. 为所有处理器使用
    #[handler]
  2. 保持处理器专注于单一职责
  3. 使用合适的返回类型(Json、Text、StatusCode)
  4. 使用Result类型处理错误
  5. 为基础HTTP服务器使用
    TcpListener
  6. 使用
    hoop()
    将通用逻辑提取到中间件中