### Clone Salvo repository and run example applications Source: https://salvo.rs/zh-hans/guide/quick-start Downloads the official Salvo repository containing comprehensive examples demonstrating various framework features. Uses Cargo's --bin flag to run specific example binaries. All examples follow the naming pattern example- and can be executed from the examples directory after cloning. ```shell git clone https://github.com/salvo-rs/salvo cd salvo/examples cargo run --bin example-hello ``` -------------------------------- ### Install Salvo CLI tool using Cargo Source: https://salvo.rs/zh-hans/guide/quick-start Installs the Salvo command-line interface globally via Cargo. The CLI tool accelerates project scaffolding by generating boilerplate code and standardized project structures. Requires active internet connection and Cargo package manager in your system PATH. ```shell cargo install salvo-cli ``` -------------------------------- ### Generate new Salvo project with CLI command Source: https://salvo.rs/zh-hans/guide/quick-start Creates a new Salvo web application project with a single command. The CLI generates a complete project structure with pre-configured Cargo.toml, source files, and example handlers. Replace 'project_name' with your desired project name to initialize a ready-to-develop application. ```shell salvo new project_name ``` -------------------------------- ### Create basic Salvo HTTP server with greeting handlers Source: https://salvo.rs/zh-hans/guide/quick-start Implements a complete Salvo web server with two route handlers for English and Chinese greetings. Demonstrates handler functions with different return types (direct string and Result type), router configuration, TCP listener binding to port 5800, and tracing initialization. Serves as a minimal working example of a Salvo application. ```rust use salvo::prelude::*; // Handler for English greeting #[handler] async fn hello() -> &'static str { "Hello World" } // Handler for Chinese greeting #[handler] async fn hello_zh() -> Result<&'static str, ()> { Ok("你好,世界!") } #[tokio::main] async fn main() { // Initialize logging subsystem tracing_subscriber::fmt().init(); // Bind server to port 5800 let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; // Create router with two endpoints: // - / (root path) returns English greeting // - /你好 returns Chinese greeting let router = Router::new() .get(hello) .push(Router::with_path("你好").get(hello_zh)); // Print router structure for debugging println!("{:?}", router); // Start serving requests Server::new(acceptor).serve(router).await; } ``` -------------------------------- ### Configure Cargo.toml with Salvo dependencies Source: https://salvo.rs/zh-hans/guide/quick-start Defines project metadata and dependencies for a basic Salvo web application. Specifies salvo framework version 0.80.0, tokio async runtime with macros feature, and tracing utilities for logging. Required as the foundation for compiling and running Salvo-based HTTP servers. ```toml [package] name = "example-hello" version = "0.1.0" edition = "2024" [dependencies] salvo = { version = "0.80.0" } tokio = { version = "1", features = ["macros"] } tracing = "0.1" tracing-subscriber = "0.3" ``` -------------------------------- ### Cache Middleware Implementation: Actix-web vs. Salvo Source: https://salvo.rs/zh-hans/guide/features/cache This example contrasts the cache middleware implementation between Actix-web and Salvo. It shows how to initialize a cache with a time-to-live (TTL) in Actix-web and the equivalent setup in Salvo using `MokaStore` and `RequestIssuer`. ```rust // Actix-web 缓存示例 use actix_web_cache::Cache; App::new().wrap(Cache::new().ttl(30)) ``` ```rust // Salvo 对应实现 use salvo::prelude::*; Router::new().hoop(Cache::new(MokaStore::new(100), RequestIssuer::new())) ``` -------------------------------- ### Salvo Integration Example with Tower RateLimitLayer Source: https://salvo.rs/zh-hans/guide/features/tower-compat Demonstrates how to use Tower's RateLimitLayer within a Salvo application. The example shows converting the Tower Layer to a Salvo-compatible format using `.compat()` and applying it as a hoop (middleware) to a router. It includes setup for tracing and running the Salvo server. ```rust use salvo::prelude::*; use tokio::time::Duration; use tower::limit::RateLimitLayer; #[handler] async fn hello() -> &'static str { "Hello World" } #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); let limit = RateLimitLayer::new(5, Duration::from_secs(30)).compat(); let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; let router = Router::new().hoop(limit).get(hello); Server::new(acceptor).serve(router).await; } ``` -------------------------------- ### Salvo Test Example (Rust) Source: https://salvo.rs/zh-hans/guide/topics/testing This snippet provides a basic "Hello World" test example using the Salvo testing module in Rust. It demonstrates setting up a simple route and verifying the response. This example requires the `salvo` and `tokio` crates. ```Rust use salvo::prelude::*; #[handler] async fn hello_world() -> &'static str { "Hello World" } #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); tracing::info!("Listening on http://127.0.0.1:5800"); let acceptor = TcpListener::new("127.0.0.1:5800").bind().await; Server::new(acceptor).serve(route()).await; } fn route() -> Router { Router::new().get(hello_world) } #[cfg(test)] mod tests { use salvo::prelude::*; use salvo::test::{ResponseExt, TestClient}; #[tokio::test] async fn test_hello_world() { let service = Service::new(super::route()); let content = TestClient::get(format!("http://127.0.0.1:5800/")) .send(&service) .await .take_string() .await .unwrap(); assert_eq!(content, "Hello World"); } } ``` -------------------------------- ### Dynamic Rate Limiter Example in Rust Source: https://salvo.rs/zh-hans/guide/features/rate-limiter Illustrates dynamic rate limiting in Salvo, allowing quotas to be fetched and adjusted at runtime. This example uses a SlidingGuard, MokaStore, a custom UserIssuer to get user identifiers from query parameters, and a CustomQuotaGetter to retrieve user-specific quotas from a static HashMap. It serves an HTML page with links to demonstrate different user-based rate limits. ```rust use std::borrow::Borrow; use std::collections::HashMap; use std::hash::Hash; use std::sync::LazyLock; use salvo::Error; use salvo::prelude::*; use salvo::rate_limiter::{ CelledQuota, MokaStore, QuotaGetter, RateIssuer, RateLimiter, SlidingGuard, }; static USER_QUOTAS: LazyLock> = LazyLock::new(|| { let mut map = HashMap::new(); map.insert("user1".into(), CelledQuota::per_second(1, 1)); map.insert("user2".into(), CelledQuota::set_seconds(1, 1, 5)); map.insert("user3".into(), CelledQuota::set_seconds(1, 1, 10)); map }); struct UserIssuer; impl RateIssuer for UserIssuer { type Key = String; async fn issue(&self, req: &mut Request, _depot: &Depot) -> Option { req.query::("user") } } struct CustomQuotaGetter; impl QuotaGetter for CustomQuotaGetter { type Quota = CelledQuota; type Error = Error; async fn get(&self, key: &Q) -> Result where String: Borrow, Q: Hash + Eq + Sync, { USER_QUOTAS .get(key) .cloned() .ok_or_else(|| Error::other("user not found")) } } #[handler] async fn limited() -> &'static str { "Limited page" } #[handler] async fn home() -> Text<&'static str> { Text::Html(HOME_HTML) } #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); let limiter = RateLimiter::new( SlidingGuard::new(), MokaStore::new(), UserIssuer, CustomQuotaGetter, ); let router = Router::new() .get(home) .push(Router::with_path("limited").hoop(limiter).get(limited)); let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; Server::new(acceptor).serve(router).await; } static HOME_HTML: &str = r#" Rate Limiter Dynmaic

Rate Limiter Dynamic

This example shows how to set limit for different users.

Limited page for user1: 1/second

Limited page for user2: 1/5seconds

Limited page for user3: 1/10seconds

"#; ``` -------------------------------- ### Implement Salvo handlers with different function signatures Source: https://salvo.rs/zh-hans/guide/quick-start Demonstrates four progressive ways to implement Salvo handler functions, from the complete explicit signature to more concise forms. Shows how unused parameters can be omitted, how return types implement the Writer trait for automatic response generation, and how Result types enable proper error handling. These patterns help developers write cleaner, more maintainable handler code based on their specific needs. ```rust #[handler] async fn hello(_req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) { res.render("Hello world"); } ``` ```rust #[handler] async fn hello(res: &mut Response) { res.render("Hello world"); } ``` ```rust #[handler] async fn hello(res: &mut Response) -> &'static str { "Hello world" } ``` ```rust #[handler] async fn hello(res: &mut Response) -> Result<&'static str, ()> { Ok("Hello world") } ``` -------------------------------- ### Initiate GET Request (Async) Source: https://salvo.rs/zh-hans/guide/ecology/reqwest Demonstrates how to make a simple asynchronous GET request using the `get` shortcut and retrieve the response body as text. For multiple requests, creating a `Client` is recommended to leverage connection pooling. ```rust let body = reqwest::get("https://www.rust-lang.org") .await? .text() .await?; println!("body = {body:?}"); ``` ```rust let client = reqwest::Client::new(); let res = client.get("https://www.rust-lang.org") .send() .await?; ``` -------------------------------- ### Configure Salvo server with HTTP3 and TLS encryption Source: https://salvo.rs/zh-hans/guide/quick-start Extends Salvo server to support HTTP/3 over QUIC protocol with TLS encryption. Loads X.509 certificate and private key from embedded PEM files, configures Rustls for secure connections, and combines TCP and QUIC listeners on port 5800. Requires valid TLS certificate files in the certs directory and enables modern HTTP/3 protocol support. ```rust use salvo::conn::rustls::{Keycert, RustlsConfig}; use salvo::prelude::*; // Handler function responding with "Hello World" for HTTP/3 requests #[handler] async fn hello() -> &'static str { "Hello World" } #[tokio::main] async fn main() { // Initialize logging system tracing_subscriber::fmt().init(); // Load TLS certificate and private key from embedded PEM files let cert = include_bytes!("../certs/cert.pem").to_vec(); let key = include_bytes!("../certs/key.pem").to_vec(); // Create router with single endpoint let router = Router::new().get(hello); // Configure TLS settings using Rustls let config = RustlsConfig::new(Keycert::new().cert(cert.as_slice()).key(key.as_slice())); // Create TCP listener with TLS encryption on port 5800 let listener = TcpListener::new(("0.0.0.0", 5800)).rustls(config.clone()); // Create QUIC listener and combine with TCP listener let acceptor = QuinnListener::new(config.build_quinn_config().unwrap(), ("0.0.0.0", 5800)) .join(listener) .bind() .await; // Start server supporting both HTTP/3 (QUIC) and HTTPS (TCP) Server::new(acceptor).serve(router).await; } ``` -------------------------------- ### Basic Routing Setup in Salvo Rust Framework Source: https://salvo.rs/zh-hans/guide/index Salvo's Router provides a simple API for defining paths and HTTP methods like GET, POST, PATCH, DELETE. Parameters like {id} can be matched in paths for dynamic routing. This supports both flat and nested structures to organize routes by business logic. ```rust Router::new().path("articles").get(list_articles).post(create_article); Router::new() .path("articles/{id}") .get(show_article) .patch(edit_article) .delete(delete_article); ``` ```rust Router::new() .path("articles") .get(list_articles) .push(Router::new().path("{id}").get(show_article)); ``` ```rust Router::new() .path("articles") .hoop(auth_check) .post(list_articles) .push(Router::new().path("{id}").patch(edit_article).delete(delete_article)); ``` ```rust Router::new() .push( Router::new() .path("articles") .get(list_articles) .push(Router::new().path("{id}").get(show_article)), ) .push( Router::new() .path("articles") .hoop(auth_check) .post(list_articles) .push(Router::new().path("{id}").patch(edit_article).delete(delete_article)), ); ``` -------------------------------- ### Static Rate Limiter Example in Rust Source: https://salvo.rs/zh-hans/guide/features/rate-limiter Demonstrates how to implement a static rate limiter using Salvo. This example configures a rate limiter with a FixedGuard, MokaStore, RemoteIpIssuer, and a basic quota of 1 request per second. It sets up a simple HTTP server that responds with 'Hello World'. ```rust use salvo::prelude::*; use salvo::rate_limiter::{BasicQuota, FixedGuard, MokaStore, RateLimiter, RemoteIpIssuer}; #[handler] async fn hello() -> &'static str { "Hello World" } #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); let limiter = RateLimiter::new( FixedGuard::new(), MokaStore::new(), RemoteIpIssuer, BasicQuota::per_second(1), ); let router = Router::with_hoop(limiter).get(hello); let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; Server::new(acceptor).serve(router).await; } ``` -------------------------------- ### Rust WebSocket Example with Salvo Source: https://salvo.rs/zh-hans/guide/features/websocket This code shows how to create a WebSocket server using the Salvo framework in Rust. It handles incoming WebSocket connections, receives messages from the client, and sends them back. The example also includes basic HTML for a client-side WebSocket connection. ```Rust use salvo::prelude::*; use salvo::websocket::WebSocketUpgrade; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize)] struct User { id: usize, name: String, } #[handler] async fn connect(req: &mut Request, res: &mut Response) -> Result<(), StatusError> { let user = req.parse_queries::(); WebSocketUpgrade::new() .upgrade(req, res, |mut ws| async move { println!("{user:#? } "); while let Some(msg) = ws.recv().await { let msg = if let Ok(msg) = msg { msg } else { // client disconnected return; }; if ws.send(msg).await.is_err() { // client disconnected return; } } }) .await } #[handler] async fn index(res: &mut Response) { res.render(Text::Html(INDEX_HTML)); } #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); let router = Router::new() .get(index) .push(Router::with_path("ws").goal(connect)); let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; Server::new(acceptor).serve(router).await; } static INDEX_HTML: &str = r#" WS

WS

Connecting...

"#; ``` -------------------------------- ### Salvo Router Definition (Example 1) Source: https://salvo.rs/zh-hans/guide/concepts/router This snippet demonstrates a basic router definition using `Router::with_path`. ```rust Router::with_path("articles").get(list_articles).post(create_article); ``` -------------------------------- ### Writing Salvo Connection Handler in Rust Source: https://salvo.rs/zh-hans/guide/features/third-party Define the main function to initialize tracing, set up the Salvo router with a WebSocket path, and start the server on localhost:5800. The user_connected handler parses User from query params and upgrades the connection using WebSocketUpgrade, invoking handle_socket on success. Inputs are HTTP requests with queries; outputs are WebSocket streams or bad request errors; requires tokio and salvo crates. ```rust #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); let router = Router::new() .push(Router::with_path("chat").handle(user_connected)); tracing::info!("Listening on http://127.0.0.1:5800"); let acceptor = TcpListener::new("127.0.0.1:5800").bind().await; Server::new(acceptor).serve(router).await; } #[handler] async fn user_connected(req: &mut Request, res: &mut Response) -> Result<(), StatusError> { let user: Result = req.parse_queries(); match user { Ok(user) => { WebSocketUpgrade::new().upgrade(req, res, |ws| async move { handle_socket(ws, user).await; }).await } Err(_err) => { Err(StatusError::bad_request()) } } } ``` -------------------------------- ### Middleware Example in Salvo Source: https://salvo.rs/zh-hans/guide/concepts/handler Demonstrates a basic middleware structure in Salvo using the FlowCtrl to handle requests and responses. Shows the pre-processing and post-processing phases around the call_next method. ```Rust async fn example_middleware(req: &mut Request, depot: &mut Depot,resp: &mut Response, ctrl: &mut FlowCtrl) { // 前处理(请求阶段) // 在这里放置请求进入时需要执行的逻辑 // 调用链中的下一个处理器 ctrl.call_next(req, depot,resp).await; // 后处理(响应阶段) // 在这里放置请求处理完成后需要执行的逻辑 } ``` -------------------------------- ### Salvo Rust: Set and Get Data using insert/get Source: https://salvo.rs/zh-hans/guide/concepts/depot Demonstrates how to insert a string key-value pair into the Depot and retrieve it using `insert` and `get`. If the key does not exist or the type doesn't match, `get` returns `None`. ```rust use salvo::prelude::*; #[handler] async fn set_user(depot: &mut Depot) { depot.insert("user", "client"); } #[handler] async fn hello(depot: &mut Depot) -> String { format!( "Hello {{}}", depot.get::<&str>("user").copied().unwrap_or_default() ) } #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); let router = Router::new().hoop(set_user).goal(hello); let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; Server::new(acceptor).serve(router).await; } ``` ```rust depot.insert("a", "b"); assert_eq!(depot.get::<&str>("a").copied().unwrap(), "b") ``` -------------------------------- ### Configure Static File Server in Salvo Source: https://salvo.rs/zh-hans/guide/features Provides an example of setting up a static file server in Salvo using the `StaticFileResponse` handler. This allows the application to serve static assets like HTML, CSS, JavaScript, and images directly from a specified directory. ```rust use salvo::prelude::*; use salvo::utils::StaticFileResponse; #[tokio::main] async fn main() { // Serve static files from the "./static" directory. // Requests to paths like "/index.html" will look for "./static/index.html". let app = Service::new() .get("/files/*path", StaticFileResponse::new("./static/")); Server::new(TcpListener::bind("127.0.0.1:3000")).serve(app).await; } ``` -------------------------------- ### Route Filtering Example with Multiple Paths Source: https://salvo.rs/zh-hans/guide/concepts/router Illustrates how to handle different access requirements for the same base path (e.g., 'articles/') by nesting routers with different middleware. ```APIDOC ## Route Filtering Example This example shows how to handle different access levels for paths under the same base URL by organizing routes into different router structures with distinct middleware. ### Example: Articles with and without Authentication ```rust Router::new() .push( Router::new() .path("articles") .get(list_articles) .push(Router::new().path("{id}").get(show_article)), ) .push( Router::new() .path("articles") .hoop(auth_check) // Apply authentication middleware here .post(create_article) // Assuming POST requires auth .push(Router::new().path("{id}").patch(edit_article).delete(delete_article)), // PATCH and DELETE also require auth ); ``` In this setup, public access to articles (GET requests) is handled by one router structure, while protected actions (POST, PATCH, DELETE) are grouped under another router with `auth_check` middleware. ``` -------------------------------- ### Salvo Router Definition (Example 2 - Flattened) Source: https://salvo.rs/zh-hans/guide/concepts/router This snippet illustrates defining routes in a flattened style using multiple `Router::with_path` calls. ```rust Router::with_path("writers").get(list_writers).post(create_writer); ``` ```rust Router::with_path("writers/{id}").get(show_writer).patch(edit_writer).delete(delete_writer); ``` ```rust Router::with_path("writers/{id}/articles").get(list_writer_articles); ``` -------------------------------- ### Adding Salvo WebSocket Dependency Source: https://salvo.rs/zh-hans/guide/features/third-party Add the salvo-websocket crate to your project's Cargo.toml to enable WebSocket support in Salvo. This dependency version 0.0.4 integrates seamlessly with Salvo's routing system. No additional setup is required beyond including it in dependencies. ```toml [dependencies] salvo-websocket = "0.0.4" ``` -------------------------------- ### Custom Wisp for Path Filters in Rust Source: https://salvo.rs/zh-hans/guide/concepts/router Demonstrates how to register custom wisp (shortcut) for frequently used path filters in Salvo.rs. This example shows registering a GUID pattern for reuse in multiple routes. ```rust Router::with_path("/articles/"); Router::with_path("/users/"); ``` ```rust use salvo::routing::filter::PathFilter; #[tokio::main] async fn main() { let guid = regex::Regex::new("[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}").unwrap(); PathFilter::register_wisp_regex("guid", guid); Router::new() .push(Router::with_path("/articles/{id:guid}").get(show_article)) .push(Router::with_path("/users/{id:guid}").get(show_user)); } ``` -------------------------------- ### Method Filters in Rust Source: https://salvo.rs/zh-hans/guide/concepts/router Shows how to use method filters to match HTTP request methods in Salvo.rs. This example demonstrates the shorthand methods for common HTTP methods and their equivalent filter usage. ```rust Router::new().get(show_article).patch(update_article).delete(delete_article); ``` ```rust use salvo::routing::filter; let mut root_router = Router::new(); let show_router = Router::with_filter(filters::get()).handle(show_article); let update_router = Router::with_filter(filters::patch()).handle(update_article); let delete_router = Router::with_filter(filters::get()).handle(delete_article); Router::new().push(show_router).push(update_router).push(delete_router); ``` -------------------------------- ### Implementing Graceful Shutdown in Rust Source: https://salvo.rs/zh-hans/guide/topics/graceful-shutdown This Rust example shows how to implement graceful shutdown in the Salvo web framework. It depends on salvo_core and tokio crates for server and async operations. The code sets up a TCP listener, starts the server, and after 60 seconds, initiates graceful shutdown without a timeout, allowing all requests to complete before closing. ```rust use salvo_core::prelude::*; #[tokio::main] async fn main() { let acceptor = TcpListener::new("127.0.0.1:5800").bind().await; let server = Server::new(acceptor); let handle = server.handle(); // 优雅地关闭服务器 tokio::spawn(async move { tokio::time::sleep(std::time::Duration::from_secs(60)).await; handle.stop_graceful(None); }); server.serve(Router::new()).await; } ``` -------------------------------- ### Implement WebSocket Communication in Salvo Source: https://salvo.rs/zh-hans/guide/features Provides a basic example of setting up WebSocket endpoints in Salvo. WebSockets enable real-time, bidirectional communication between the client and server, essential for chat applications, live notifications, and collaborative tools. ```rust use salvo::prelude::*; // Assuming a WebSocket implementation is available, e.g., via a crate // use salvo_websocket::{WebSocket, WebSocketUpgrade}; #[tokio::main] async fn main() { let app = Service::new() .get("/chat", handle_chat_connection); Server::new(TcpListener::bind("127.0.0.1:3000")).serve(app).await; } // Placeholder for WebSocket handler logic async fn handle_chat_connection(req: &mut Request, res: &mut Response) { // This function would typically use a WebSocket upgrade mechanism // provided by a Salvo-compatible WebSocket library. // For example: // if let Some(ws) = WebSocket::accept(req).await { // // Spawn a task to handle messages for this client // tokio::spawn(handle_websocket_messages(ws)); // } res.send("WebSocket connection handling logic goes here."); } // async fn handle_websocket_messages(ws: WebSocket) { // // ... implementation to receive and send messages ... // } ``` -------------------------------- ### Configure WebTransport server in Salvo (Rust) Source: https://salvo.rs/zh-hans/guide/features/webtransport Shows how to configure a Salvo server with TLS certificates and QUIC listener for WebTransport support. Requires rustls configuration. ```rust let cert = include_bytes!("../certs/cert.pem").to_vec(); let key = include_bytes!("../certs/key.pem").to_vec(); // 配置路由 let router = Router::new().push(Router::with_path("counter").goal(connect)); // 配置 TLS let config = RustlsConfig::new(Keycert::new().cert(cert.as_slice()).key(key.as_slice())); // 设置监听器 let listener = TcpListener::new(("0.0.0.0", 5800)).rustls(config.clone()); let acceptor = QuinnListener::new(config, ("0.0.0.0", 5800)) .join(listener) .bind() .await; // 启动服务器 Server::new(acceptor).serve(router).await; ``` -------------------------------- ### Configure TLS Options (Async) Source: https://salvo.rs/zh-hans/guide/ecology/reqwest Provides examples of configuring TLS settings, such as adding custom root certificates or setting client certificates for mutual TLS authentication. These options are set during client building. ```rust // 添加额外的服务器证书 let cert = reqwest::Certificate::from_pem(&cert_bytes)?; let client = reqwest::Client::builder() .add_root_certificate(cert) .build()?; ``` ```rust // 配置客户端证书 let identity = reqwest::Identity::from_pkcs12_der(&pkcs12_der, "password")?; let client = reqwest::Client::builder() .identity(identity) .build()?; ``` -------------------------------- ### Salvo Router Parameter Matching with Regular Expressions Source: https://salvo.rs/zh-hans/guide/concepts/router This example shows how to use regular expressions to restrict parameter matching. ```rust Router::with_path("articles/article_{id:num}/"), ``` -------------------------------- ### Salvo Router Definition (Example 3 - Tree-like) Source: https://salvo.rs/zh-hans/guide/concepts/router This snippet shows how to define routes in a tree-like structure for better organization. ```rust Router::with_path("writers") .get(list_writers) .post(create_writer) .push( Router::with_path("{id}") .get(show_writer) .patch(edit_writer) .delete(delete_writer) .push(Router::with_path("articles").get(list_writer_articles)), ); ``` -------------------------------- ### Path Filters with Parameters in Rust Source: https://salvo.rs/zh-hans/guide/concepts/router Demonstrates the use of path filters with parameters in Salvo.rs. Parameters can be captured from the URL and accessed in handlers. This example shows how to define and use path parameters. ```rust Router::with_path("articles/{id}").get(show_article); Router::with_path("files/{**rest_path}").get(serve_file) ``` ```rust #[handler] pub async fn show_article(req: &mut Request) { let article_id = req.param::("id"); } #[handler] pub async fn serve_file(req: &mut Request) { let rest_path = req.param::("rest_path"); } ``` -------------------------------- ### Configure Proxy (Async) Source: https://salvo.rs/zh-hans/guide/ecology/reqwest Illustrates how to configure proxy settings for HTTP and HTTPS connections. Proxies can be set explicitly or by respecting environment variables. This example shows setting an HTTP proxy and disabling proxy usage. ```rust let proxy = reqwest::Proxy::http("https://secure.example")?; let client = reqwest::Client::builder() .proxy(proxy) .build()?; ``` ```rust let client = reqwest::Client::builder() .no_proxy() .build()?; ``` -------------------------------- ### Organize Routes with Filters in Rust Source: https://salvo.rs/zh-hans/guide/concepts/router Shows how to organize routes with filters in Salvo.rs. Filters like `path` and `method` are used to match requests. This example demonstrates splitting routes for articles that require authentication and those that do not. ```rust Router::new() .push( Router::with_path("articles") .get(list_articles) .push(Router::new().path("{id}").get(show_article)), ) .push( Router::with_path("articles") .hoop(auth_check) .post(list_articles) .push(Router::new().path("{id}").patch(edit_article).delete(delete_article)), ); ``` -------------------------------- ### Handle Response (Async) Source: https://salvo.rs/zh-hans/guide/ecology/reqwest Provides examples of processing HTTP responses, including retrieving the status code, headers, and reading the response body as text or parsing it as JSON. ```rust let client = reqwest::Client::new(); let res = client.get("https://www.rust-lang.org").send().await?; // 获取状态码 let status = res.status(); // 获取响应头 let content_type = res.headers().get("content-type").unwrap(); // 读取响应体为文本 let body = res.text().await?; // 或解析为 JSON // let json: serde_json::Value = res.json().await?; ``` -------------------------------- ### Cargo.toml Dependencies Configuration Source: https://salvo.rs/zh-hans/guide/topics/handle-error Cargo.toml dependencies section showing how to enable the anyhow feature for Salvo web framework. Includes both Salvo with anyhow feature and anyhow library as dependencies for comprehensive error handling. ```toml [dependencies] salvo = { version = "*", features = ["anyhow"] } anyhow = "1.0" ``` -------------------------------- ### Implementing URL Redirects with Salvo Response Source: https://salvo.rs/zh-hans/guide/concepts/response Details how to implement HTTP redirects using Salvo's `Response` struct. The example uses `Redirect::found` to create a temporary redirect (HTTP 302) to a specified URL. ```rust use salvo::prelude::*; #[handler] async fn redirect(res: &mut Response) { res.render(Redirect::found("https://salvo.rs/")); } ``` -------------------------------- ### Salvo Rust: Set and Get Data using inject/obtain Source: https://salvo.rs/zh-hans/guide/concepts/depot Illustrates using `inject` to insert a value based on its type and `obtain` to retrieve it without needing a specific key. This is useful for singleton-like data within a request. ```rust depot.inject(Config::new()); depot.obtain::(); ``` -------------------------------- ### Implement Basic Authentication (Basic Auth) in Salvo Source: https://salvo.rs/zh-hans/guide/features Provides an example of implementing Basic Authentication in Salvo. Basic Auth is a simple HTTP authentication scheme used to validate credentials (username and password) sent by the client. This middleware is crucial for protecting routes that require user authentication. ```rust use salvo::prelude::*; use salvo::utils::basic_auth; #[tokio::main] async fn main() { let app = Service::new() .get("/", hello) .get("/protected", basic_auth("user", "password", protected)); Server::new(TcpListener::bind("127.0.0.1:3000")).serve(app).await; } #[fn_handler] async fn hello(_req: &mut Request, res: &mut Response) { res.send("Hello!"); } #[fn_handler] async fn protected(_req: &mut Request, res: &mut Response) { res.send("Protected!"); } ``` -------------------------------- ### Defining WS Connection Query Params in Rust Source: https://salvo.rs/zh-hans/guide/features/third-party Create a User struct to capture query parameters like name and room during WebSocket connection. It uses Serde for deserialization from query strings. This struct is Clone and Debug for handling in async contexts; limitations include only basic string fields without validation. ```rust #[derive(Debug, Clone, Deserialize)] struct User { name: String, room: String, } ``` -------------------------------- ### Retrieving Query Parameters in Salvo Source: https://salvo.rs/zh-hans/guide/concepts/request Demonstrates how to get a specific query parameter from the request using the `get_query` method. This method is straightforward for accessing single query values. The example shows retrieving a parameter named 'id'. ```rust req.query::("id"); ``` -------------------------------- ### Router Basics Source: https://salvo.rs/zh-hans/guide/concepts/router Demonstrates the basic structure of defining routes using `Router::with_path` and associating HTTP methods with handlers. ```APIDOC ## POST /api/articles ### Description This endpoint allows for the creation of new articles. ### Method POST ### Endpoint /api/articles ### Parameters #### Request Body - **title** (string) - Required - The title of the article. - **content** (string) - Required - The content of the article. ### Request Example ```json { "title": "My First Article", "content": "This is the content of my first article." } ``` ### Response #### Success Response (201) - **id** (string) - The unique identifier of the created article. #### Response Example ```json { "id": "article_123" } ``` ``` -------------------------------- ### 构建 Docker 镜像命令 Source: https://salvo.rs/zh-hans/guide/topics/deployment 使用 docker build 命令构建镜像。需要将 your-app-name 替换为实际镜像名称。此步骤将在本地生成可运行的 Docker 镜像。 ```bash docker build -t your-app-name . ``` -------------------------------- ### SSE Counter Stream Handler in Salvo Rust Source: https://salvo.rs/zh-hans/guide/features/sse This example demonstrates creating an SSE endpoint in Salvo that pushes incrementing counter values every second to the client. It uses Tokio for async timing and futures for streaming, with built-in reconnection. Inputs: HTTP GET request to /ticks; Outputs: SSE stream of numeric events; Limitations: Single-directional, requires Rust 1.56+ and Salvo dependencies. ```Rust // Copyright (c) 2018-2020 Sean McArthur // Licensed under the MIT license http://opensource.org/licenses/MIT //port from https://github.com/seanmonstar/warp/blob/master/examples/sse.rs use std::convert::Infallible; use std::time::Duration; use futures_util::StreamExt; use salvo::prelude::*; use salvo::sse::{self, SseEvent}; use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; // create server-sent event fn sse_counter(counter: u64) -> Result { Ok(SseEvent::default().text(counter.to_string())) } #[handler] async fn handle_tick(res: &mut Response) { let event_stream = { let mut counter: u64 = 0; let interval = interval(Duration::from_secs(1)); let stream = IntervalStream::new(interval); stream.map(move |_| { counter += 1; sse_counter(counter) }) }; sse::stream(res, event_stream); } #[tokio::main] async fn main() { tracing_subscriber::fmt().init(); let router = Router::with_path("ticks").get(handle_tick); let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; Server::new(acceptor).serve(router).await; } ``` -------------------------------- ### 运行 Docker 容器命令 Source: https://salvo.rs/zh-hans/guide/topics/deployment 使用 docker run 命令运行容器。需将端口号 5800 替换为应用实际监听的端口。容器将监听主机的指定端口。 ```bash docker run -p 5800:5800 your-app-name ``` -------------------------------- ### Rust: 自定义 404 错误处理程序 Source: https://salvo.rs/zh-hans/guide/concepts/catcher 此代码片段展示了如何在 Salvo 框架中创建一个自定义的 404 Not Found 错误处理程序。该处理程序会检查响应状态码,如果为 404,则渲染自定义错误页面并跳过后续的错误处理程序。它依赖于 `salvo::catcher::Catcher`, `salvo::prelude::*`, `tokio` 以及自定义的 `handle404` Handler。 ```rust use salvo::catcher::Catcher; use salvo::prelude::*; // Handler that returns a simple "Hello World" response #[handler] async fn hello() -> &'static str { "Hello World" } // Handler that deliberately returns a 500 Internal Server Error #[handler] async fn error500(res: &mut Response) { res.status_code(StatusCode::INTERNAL_SERVER_ERROR); } #[tokio::main] async fn main() { // Initialize logging system tracing_subscriber::fmt().init(); // Create and start server with custom error handling let acceptor = TcpListener::new("0.0.0.0:5800").bind().await; Server::new(acceptor).serve(create_service()).await; } // Create service with custom error handling fn create_service() -> Service { // Set up router with two endpoints: // - / : Returns "Hello World" // - /500 : Triggers a 500 error let router = Router::new() .get(hello) .push(Router::with_path("500").get(error500)); // Add custom error catcher for 404 Not Found errors Service::new(router).catcher(Catcher::default().hoop(handle404)) } // Custom handler for 404 Not Found errors #[handler] async fn handle404(&self, _req: &Request, _depot: &Depot, res: &mut Response, ctrl: &mut FlowCtrl) { // Check if the error is a 404 Not Found if StatusCode::NOT_FOUND == res.status_code.unwrap_or(StatusCode::NOT_FOUND) { // Return custom error page res.render("Custom 404 Error Page"); // Skip remaining error handlers ctrl.skip_rest(); } } ``` -------------------------------- ### Error Handling in Handlers Source: https://salvo.rs/zh-hans/guide/concepts/handler Provides examples of error handling within Salvo Handlers, including custom error types and integration with anyhow for simplified error management. ```Rust use salvo::anyhow; use salvo::prelude::*; struct CustomError; #[async_trait] impl Writer for CustomError { async fn write(mut self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) { res.status_code(StatusCode::INTERNAL_SERVER_ERROR); res.render("custom error"); } } #[handler] async fn handle_anyhow() -> Result<(), anyhow::Error> { Err(anyhow::anyhow!("anyhow error")) } #[handler] async fn handle_custom() -> Result<(), CustomError> { Err(CustomError) } #[tokio::main] async fn main() { let router = Router::new() .push(Router::new().path("anyhow").get(handle_anyhow)) .push(Router::new().path("custom").get(handle_custom)); let acceptor = TcpListener::new("127.0.0.1:5800").bind().await; Server::new(acceptor).serve(router).await; } ``` -------------------------------- ### 构建并运行 Salvo Docker 应用 Source: https://salvo.rs/zh-hans/guide/topics/deployment 该 Dockerfile 使用多阶段构建方式打包 Salvo 应用,构建阶段使用 Rust 镜像编译程序,运行阶段使用轻量级 Debian 镜像。适用于生产环境部署。输出为一个轻量、安全的容器镜像。 ```docker # 构建阶段 FROM rust:slim AS build WORKDIR /app # 复制依赖文件先构建依赖(利用缓存层) COPY Cargo.toml Cargo.lock ./ RUN mkdir src && \ echo 'fn main() { println!("Placeholder"); }' > src/main.rs && \ cargo build --release # 复制实际源代码并构建应用 COPY src ./src/ RUN touch src/main.rs && \ cargo build --release # 运行阶段使用精简基础镜像 FROM debian:bookworm-slim RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* # 创建非root用户运行应用 RUN useradd -ms /bin/bash appuser USER appuser WORKDIR /app # 从构建阶段复制二进制文件 COPY --from=build /app/target/release/your_app_name ./app # 设置容器启动命令 CMD ["./app"] ``` -------------------------------- ### Generate OpenAPI Documentation in Salvo Source: https://salvo.rs/zh-hans/guide/features Shows how to generate OpenAPI (Swagger) documentation for Salvo applications. This involves using attributes and specific extractors to automatically document API endpoints, parameters, and schemas, making the API discoverable and easier to integrate with. ```rust use salvo::prelude::*; use salvo_openapi::{OpenApi, OpenAPI}; // Assuming salvo_openapi crate is used #[derive(OpenAPI, Debug, Clone)] struct MyResponse { message: String, } #[tokio::main] async fn main() { let mut doc = OpenAPI::new("My API", "1.0.0"); let app = Service::new() .get("/hello", hello) .get("/openapi.json", doc.clone()); // Mount OpenAPI schema async fn hello(res: &mut Response) { res.render(Json(MyResponse { message: "Hello, OpenAPI!".to_string() })); } // Add endpoint documentation to the OpenAPI schema doc.add_route(Route::new().get("/hello").get_handler(hello)); Server::new(TcpListener::bind("127.0.0.1:3000")).serve(app).await; } ``` -------------------------------- ### Combine Filters with Logical Operators in Rust Source: https://salvo.rs/zh-hans/guide/concepts/router Illustrates how to combine filters using logical operators like `and` and `or` in Salvo.rs. This example shows a route that matches both a specific path and HTTP method. ```rust Router::with_filter(filters::path("hello").and(filters::get())); ```