Code Monkey home page Code Monkey logo

salvo's Introduction

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 and Tokio;
  • Unified middleware and handle interface;
  • Routing supports multi-level nesting, and middleware can be added at any level;
  • Integrated Multipart form processing;
  • Support Websocket;
  • Acme support, automatically get TLS certificate from let's encrypt;
  • Supports mapping from multiple local directories into one virtual directory to provide services.

⚡️ Quick start

You can view samples here, or view offical website.

Create a new rust project:

cargo new hello_salvo --bin

Add this to Cargo.toml

[dependencies]
salvo = "0.23"
tokio = "1"

Create a simple function handler in the main.rs file, we call it hello_world, this function just render plain text "Hello World".

use salvo::prelude::*;

#[fn_handler]
async fn hello_world(res: &mut Response) {
    res.render(Text::Plain("Hello World"));
}

In the main function, we need to create a root Router first, and then create a server and call it's bind function:

use salvo::prelude::*;

#[fn_handler]
async fn hello_world() -> &'static str {
    "Hello World"
}
#[tokio::main]
async fn main() {
    let router = Router::new().get(hello_world);
    Server::new(TcpListener::bind("127.0.0.1:7878")).serve(router).await;
}

Middleware

There is no difference between Handler and Middleware, Middleware is just Handler. So you can write middlewares without to know concpets like associated type, generic type. You can write middleware if you can write function!!!

use salvo::http::header::{self, HeaderValue};
use salvo::prelude::*;

#[fn_handler]
async fn add_header(res: &mut Response) {
    res.headers_mut()
        .insert(header::SERVER, HeaderValue::from_static("Salvo"));
}

Then add it to router:

Router::new().hoop(add_header).get(hello_world)

This is a very simple middleware, it add Header to Response, View full source code.

Chainable tree routing system

Normally we write routing like this:

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:

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:

Router::with_path("articles")
    .hoop(auth_check)
    .post(list_articles)
    .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:

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)
            .post(list_articles)
            .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:

PathFilter::register_part_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:

Router::with_path("<id:guid>").get(index)

View full source code

File upload

We can get file async by the function file in Request:

#[fn_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.set_status_code(StatusCode::INTERNAL_SERVER_ERROR);
        } else {
            res.render("Ok");
        }
    } else {
        res.set_status_code(StatusCode::BAD_REQUEST);
    }
}

More Examples

Your can find more examples in examples folder. You can run these examples with the following command:

cargo run --bin --example-basic_auth

You can use any example name you want to run instead of basic_auth here.

There is a real and open source project use Salvo: https://github.com/driftluo/myblog.

🚀 Performance

Benchmark testing result can be found from here:

https://web-frameworks-benchmark.netlify.app/result?l=rust

https://www.techempower.com/benchmarks/#section=test&runid=785f3715-0f93-443c-8de0-10dca9424049 techempower

🩸 Contributing

Contributions are absolutely, positively welcome and encouraged! Contributions come in many forms. You could:

  • Submit a feature request or bug report as an issue;
  • Comment on issues that require feedback;
  • Contribute code via pull requests;
  • Publish Salvo-related technical articles on blogs or technical platforms。

All pull requests are code reviewed and tested by the CI. Note that unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Salvo by you shall be dual licensed under the MIT License, without any additional terms or conditions.

☕ Supporters

Salvo is an open source project. If you want to support Salvo, you can ☕ buy a coffee here.

⚠️ License

Salvo is licensed under either of

salvo's People

Contributors

biluohc avatar chrislearn avatar depth-liu avatar driftluo avatar fergus-hou avatar github-actions[bot] avatar siwilizhao avatar ww156 avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.