Salvo Web Framework

repository·main·Indexed 26 days ago

https://github.com/salvo-rs/salvo

A powerful, simple, and high-performance Rust web framework built on Hyper and Tokio. Salvo features tree-based routing, first-class OpenAPI support, automatic TLS via ACME, and built-in support for HTTP/1, HTTP/2, HTTP/3, WebSockets, and WebTransport. It includes a variety of middleware for CORS, compression, CSRF, flash messages, JWT authentication, and OpenTelemetry integration.

Tokens
39.3K
Snippets
109
Records
185
Agent score
87%

What's inside Salvo

  1. Use salvo-proxy for request forwarding

    main
    The salvo-proxy crate allows you to forward requests to upstream servers, making it suitable for API gateways, load balancers, and reverse proxies. It supports HTTP/HTTPS, WebSocket connections, header manipulation, and multiple client backends like Hyper and Reqwest.
  2. Serve static files and directories with salvo-serve-static

    main

    Use salvo-serve-static to serve static files and directories within a Salvo web application.

    Key capabilities include:

    • Automatic Content Type Detection: Automatically detects and sets the correct Content-Type header for files.
    • Directory Listing: Supports directory browsing with multiple output formats: HTML, JSON, XML, and Text.
    • Compression Support: Serves compressed file variants including Brotli, Gzip, Deflate, and Zstd.
    • Embedded Files: Integrates with rust-embed to serve files embedded directly into your Rust binary.
  3. Explore community maintained Salvo modules

    main

    The Salvo ecosystem includes several community-maintained modules for extending functionality:

    • Socketioxide: A socket.io server implementation in Rust that integrates with the Tower ecosystem and the Tokio stack.
    • Websocket: A websocket tool specifically for Salvo.
    • protect-endpoints: A collection of crates designed to protect your endpoints.
    • salvo-captcha: A captcha middleware for the Salvo framework.
    • salvo-casbin: An access control hook for Salvo using Casbin.
    • SalvoRsTool: An Idea RustRover plugin for quickly generating DTO, router, and service template code.
  4. Enable OpenAPI support in Salvo

    main

    To use OpenAPI support in your Salvo project, you must enable the oapi feature in your Cargo.toml file. This provides the necessary macros and functionality to generate OpenAPI documentation for your web services.

    salvo = { version = "*", features = ["oapi"] }
  5. Install salvo-jwt-auth

    main

    To use JWT authentication, enable the jwt-auth feature in your Cargo.toml.

    By default, Salvo uses aws-lc-rs as the cryptography provider. If you prefer the RustCrypto provider, disable default features and enable jwt-auth-ring instead.

    # Default (AWS-LC)
    salvo = { version = "*", features = ["jwt-auth"] }
    
    # Using RustCrypto (Ring)
    salvo = { version = "*", default-features = false, features = [
        "server",
        "http1",
        "ring",
        "jwt-auth-ring",
    ] }
  6. Implement Tree Routing with Middleware

    main

    Salvo supports tree-based routing where you can push sub-routers to a parent router. This allows you to apply specific middleware (using .hoop()) to entire branches of your API (e.g., applying authentication only to certain paths).

    Router::new()
        // Public routes
        .push(Router::with_path("articles").get(list_articles))
        // Routes requiring authentication
        .push(Router::with_path("articles").hoop(auth_check).post(create_article).delete(delete_article))
  7. Build JSON APIs with Salvo

    main

    Salvo handlers can automatically deserialize request bodies and return typed JSON responses using serde.

    To use this pattern, ensure you have serde with the derive feature added to your project:

    cargo add serde --features derive

    Example of a JSON POST handler:

    use salvo::http::ParseError;
    use salvo::prelude::*;
    use serde::{Deserialize, Serialize};
    
    #[derive(Deserialize)]
    struct CreateTodo {
        text: String,
    }
    
    #[derive(Serialize)]
    struct Todo {
        id: u64,
        text: String,
    }
    
    #[handler]
    async fn create_todo(req: &mut Request) -> Result<Json<Todo>, ParseError> {
        let payload = req.parse_json::<CreateTodo>().await?;
        Ok(Json(Todo {
            id: 1,
            text: payload.text,
        }))
    }
  8. Build JSON APIs with Serde

    main

    Handlers can deserialize request bodies and return typed JSON responses. To use this pattern, you must add serde with the derive feature to your project.

    1. Add dependency:
    cargo add serde --features derive
    1. Example implementation:
    use salvo::http::ParseError;
    use salvo::prelude::*;
    use serde::{Deserialize, Serialize};
    
    #[derive(Deserialize)]
    struct CreateTodo {
        text: String,
    }
    
    #[derive(Serialize)]
    struct Todo {
        id: u64,
        text: String,
    }
    
    #[handler]
    async fn create_todo(req: &mut Request) -> Result<Json<Todo>, ParseError> {
        let payload = req.parse_json::<CreateTodo>().await?;
        Ok(Json(Todo {
            id: 1,
            text: payload.text,
        }))
    }
  9. Generate OpenAPI parameters from structs using `ToParameters`

    main

    The #[derive(ToParameters)] macro allows you to define OpenAPI parameters directly from a Rust struct. This eliminates the need to manually define path parameters within the #[salvo_oapi::endpoint(...parameters(...))] section when using structs to represent them.

    Key features:

    • Doc Comments: Doc comments on struct fields are automatically used as the description in the generated OpenAPI spec.
    • Deprecation: Using Rust's #[deprecated] attribute on a field will mark it as deprecated in the OpenAPI spec (as a boolean flag).
    • Inlining: You can inline schemas for specific fields using the inline attribute.

    Note: Primitive types and String path parameters or tuple-style path parameters still need to be defined in the parameters(...) section of the endpoint if you require custom descriptions or non-default configurations.

    #[derive(salvo_oapi::ToParameters, serde::Deserialize)]
    struct Query {
        /// Query todo items by name.
        name: String
    }