#web-server #salvo #server-framework #sea-orm #jwt #encapsulated #config-toml

webserver-rs

An out-of-the-box web server framework that is encapsulated based on salvo and sea-orm

18 releases (4 breaking)

new 0.5.2 Apr 26, 2024
0.5.1 Apr 26, 2024
0.4.6 Apr 26, 2024
0.3.2 Apr 20, 2024
0.1.1 Apr 18, 2024

#169 in HTTP server

Download history 694/week @ 2024-04-15 918/week @ 2024-04-22

1,612 downloads per month

MIT license

33KB
510 lines

Feature

An out-of-the-box web server framework that is encapsulated based on salvo and sea-orm.

Use DataBase (sea-orm)

  1. Add sea-orm dependency in your Cargo.toml

  2. Enable one of the following features or multiple, depending on what kind of databases you need

  • mysql
  • sqlite
  • postgres
  1. Run the following command, see sea-orm documentation
  • sea-orm-cli generate entity -o src/model
  1. Import the generated model in your main.rs file
  • mod model;
  1. which databases are used is configurable in config.toml

Use Http3

Enable http3 feature and config the relevant certificates in config.toml

Example:

use webserver_rs::prelude::*;
use salvo::prelude::*;
use salvo::jwt_auth::HeaderFinder;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::io::AsyncReadExt;
use webserver_rs::{
    MemoryStream, authorization, build_cros, config::Config, expire_time, html_err,
    json_err, router, FromConfigFile, HttpResult,
};

use webserver_rs::web_core::authorization::gen_token;

#[derive(Debug, Serialize, Deserialize)]
#[serde(crate = "serde")]
struct JwtClaim {
    username: String,
    exp: i64,  // required
}

#[handler]
pub fn hello(req: &mut Request, res: &mut Response) -> HttpResult<()> {
    let c = req.query::<String>("id");
    println!("{c:?}");
    let p = req.query::<String>("id").ok_or(html_err!(
        400,
        json!({
            "status":"fail",
            "msg":"id not found"
        })
        .to_string()
    ))?;
    res.render(Text::Plain(format!("hello, {p}")));
    Ok(())
}

#[handler]
pub fn login(depot: &mut Depot, res: &mut Response) -> HttpResult<()> {
    let config = depot.obtain::<Config>().map_err(|_e| {
        crate::json_err!(400,{
            "status":"fail",
            "msg":"config not found"
        })
    })?;
    let token = gen_token(
        config.secret_key.clone(),
        JwtClaim {
            exp: crate::expire_time!(Days(1)),
            username: "a".to_string(),
        },
    )
    .map_err(|e| crate::json_err!(400, {"err":e.to_string()}))?;
    res.render(Text::Plain(token));
    Ok(())
}

#[handler]
pub async fn image(res: &mut Response) -> HttpResult<()> {
    let mut file = tokio::fs::File::open("./test.png")
        .await
        .map_err(|_| crate::html_err!(404, ""))?;
    let mut s = Vec::new();
    file.read_to_end(&mut s).await.unwrap();
    res.stream(MemoryStream::new(s, 200));
    Ok(())
}
#[handler]
pub async fn text_json(res: &mut Response) -> HttpResult<()> {
    res.render(Text::Json(
        json!({
            "title":1
        })
        .to_string(),
    ));
    Ok(())
}

mod ab {
    use salvo::handler;

    #[handler]
    pub fn show() -> &'static str {
        "abc"
    }

    pub mod shop {
        #[super::handler]
        pub fn show() -> &'static str {
            "show"
        }
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let config = Config::from_config_file("./config.toml").expect("config file not found");
    let jwt = authorization::gen_jwt_auth::<JwtClaim>(
        config.secret_key.clone(),
        vec![Box::new(HeaderFinder::new())],
    );

    webserver_rs::serve_routes! {
        config =>[
        // http://localhost:8080/hello
        router!([get, post] => @hello)
            .hoop(jwt)
            .hoop(authorization::AuthGuard::new(|_e| html_err!("unauthorized"))),
        // http://localhost:8080/user/login    
        router!([get] => /user/@login),
        // http://localhost:8080/a/b/show
        router!([get, post] => a/b/@ab::show),
        // http://localhost:8080/b/c/show/*	
        router!([get, post, put] => /b/c/@ab::show/<**path>),
        // http://localhost:8080/test_json
        router!([get, post] => @text_json).hoop(build_cros("*")),
        // http://localhost:8080/ab/shop/show    
        router!([get, post] => ...@ab::shop::show).hoop(build_cros("*")),
        ]  // (& [middlewares,])?
    };
    Ok(())
}

Dependencies

~34–57MB
~1M SLoC