Skip to content
On this page

Wrap

1、简介

Warp是一个基于Rust语言开发的Web框架,用于构建高性能的Web应用程序

Warp具有以下特点:

  • 基于Rust语言,具有高性能和安全性。
  • 提供了丰富的中间件和功能,例如路由、参数提取、请求处理、响应处理等。
  • 支持异步编程,可以利用异步IO的优势提高性能。
  • 易于使用和学习。

2、官网

文档:https://docs.rs/warp/latest/warp

示例:https://github.com/seanmonstar/warp/tree/master/examples

3、安装依赖

js
[dependencies]
tokio = { version = "1.0", features = ["full"] }
warp = "0.3.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
log = "0.4"
pretty_env_logger = "0.4.0"

4、入门

4.1、路由

4.1.1、hello

TIP

这种路由不支持异步, 所有请求方式都可以触发

js
use warp::Filter;

#[tokio::main]
async fn main() {
  let hello = warp::path!("hello"/String).map(|name| format!("Hello, {}!", name));
  warp::serve(hello).run(([127, 0, 0, 1], 3030)).await;
}

4.1.2、参数提取

接收限定类型

js
use warp::Filter;

#[tokio::main]
async fn main() {
  let hi = warp::path!("hi")
  .and(warp::path::param())
  .and(warp::header::<String>("user-agent"))
  .map(|name: String, agent| format!("Hi, {}! {}", name, agent));

  warp::serve(hi).run(([127, 0, 0, 1], 3030)).await;
}

入参限定参数类型

js
use warp::Filter;

#[tokio::main]
async fn main() {
  let hi = warp::path!("hi")
  .and(warp::path::param::<String>())
  .and(warp::header("user-agent"))
  .map(|name, agent: String| format!("Hi, {}! {}", name, agent));

  warp::serve(hi).run(([127, 0, 0, 1], 3030)).await;
}

4.1.3、日志打印

全局日志

js
async fn main() {
  std::env::set_var("RUST_LOG", "debug");
  pretty_env_logger::init_custom_env("RUST_LOG");
}

自定义日志

js
let hi_log = warp::log("hi");

let hi = warp::path("hi")
.and(warp::path::param::<String>())
.and(warp::header::<String>("user-agent"))
.map(|name, agent| format!("Hi, {}! {}", name, agent))
.with(hi_log);

4.1.4、请求方式

js
// get post put delete
let base = warp::path("base");
let base_get = base.and(warp::get()).map(|| "Hello, get!");
let base_post = base.and(warp::post()).map(|| "Hello, post!");
let base_put = base.and(warp::put()).map(|| "Hello, put!");
let base_delete = base.and(warp::delete()).map(|| "Hello, delete!");

let apis = base_get.or(base_post).or(base_put).or(base_delete);

4.1.5、多路由

js
use warp::Filter;

#[tokio::main]
async fn main() {
  std::env::set_var("RUST_LOG", "debug");
  pretty_env_logger::init_custom_env("RUST_LOG");

  let hi_log = warp::log("hi");

  let hello = warp::path!("hello"/String).map(|name| format!("Hello, {}!", name));

  let hi = warp::path("hi")
  .and(warp::path::param::<String>())
  .and(warp::header::<String>("user-agent"))
  .map(|name, agent| format!("Hi, {}! {}", name, agent))
  .with(hi_log);

  // get /
  let get = warp::path::end().map(|| "Hello, World!");
  // get /he(优先级高)
  let get2 = warp::path("he").map(|| "Hello, he!");
  // get /he/:name
  let get3 = warp::path!("he" / String).map(|name| format!("Hello, {}!", name));

  let apis = hello.or(hi).or(get).or(get3).or(get2);
  warp::serve(apis).run(([127, 0, 0, 1], 3030)).await;
}

4.1.6、同步、异步

map同步请求

js
let get = warp::path::end().map(|| "Hello, World!");

and_then异步请求

js
let sleep = warp::path("sleep")
.and(warp::get())
.and(warp::path::param::<u64>())
.and_then(sleep);

async fn sleep(seconds: u64) -> Result<impl warp::Reply, Infallible> {
  tokio::time::sleep(Duration::from_secs(seconds)).await;
  Ok(format!("I waited {} seconds!", seconds))
}

4.1.7、方法路由

js
async fn rest_get(id: u32) -> Result<Json, warp::Rejection> {
  let some_thing = json!({
    "id": id,
    "name": format!("warp{}", id),
  });
  let some_thing_warp = warp::reply::json(&some_thing);
  Ok(some_thing_warp)
}

async fn rest_list() -> Result<Json, warp::Rejection> {
  let res = json!([
    {
      "id": 1,
      "name": "warp1",
    },
    {
      "id": 2,
      "name": "warp2",
    },
  ]);
  let some_thing_warp = warp::reply::json(&res);
  Ok(some_thing_warp)
}

async fn rest_create(val: Value) -> Result<Json, warp::Rejection> {
  let some_thing_warp = warp::reply::json(&val);
  Ok(some_thing_warp)
}

fn rest_api() -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
  let rest_base = warp::path("rest");
  let get = rest_base
    .and(warp::get())
    .and(warp::path::param())
    .and_then(rest_get);
  let list = rest_base
    .and(warp::get())
    .and_then(rest_list);
  let create = rest_base
    .and(warp::post())
    .and(warp::body::json())
    .and_then(rest_create);
  get.or(list).or(create)
}

let routes = index.or(rest_api());
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;

4.2、数据处理

4.2.1、json

js
let json = warp::path("json")
.and(warp::get())
.and(warp::path::end())
.map(|| {
  warp::reply::json(&Employee{
    name: "warp".to_string(),
    rate: 100
  })
});

4.2.2、body

js
use serde_derive::{Deserialize, Serialize};
use warp::Filter;

#[derive(Deserialize, Serialize)]
struct Employee {
    name: String,
    rate: u32,
}

// post body
let promote = warp::path("employees")
.and(warp::post())
.and(warp::path::param::<u32>())
.and(warp::body::content_length_limit(1024 * 1024))
.and(warp::body::json())
.map(|rate: u32, mut employee: Employee| {
  employee.rate = rate;
  warp::reply::json(&employee)
});

4.2.3、params、query

js
async fn get_items(param: String, query: HashMap<String, String>) -> Result<impl warp::Reply, Infallible> {
  tokio::time::sleep(Duration::from_secs(1)).await;
  Ok(format!("get path: {} items: {:?}", param, query))
}

let items = warp::path("items")
  .and(warp::get())
  .and(warp::path::param::<String>())
  .and(warp::query::<HashMap<String, String>>())
  .and(warp::path::end())
  .and_then(get_items);

4.2.4、文件服务

js
let web_dir = warp::fs::dir(WEB_DIR);

let apis = web_dir;
warp::serve(apis).run(([127, 0, 0, 1], 3030)).await;
js
let index = warp::get()
  .and(warp::path::end())
  .and(warp::fs::file(format!("{}/index.html", WEB_DIR)));

let apis = index;
warp::serve(apis).run(([127, 0, 0, 1], 3030)).await;
js
let readme = warp::get()
  .and(warp::path::end())
  .and(warp::fs::file("./README.md"));

  let examples = warp::path("ex")
  .and(warp::fs::dir("./examples"));

4.2、中间件

4.2.1、登录授权

header_handler.rs

js
use warp::Filter;

const XAUTH: &str = "x-auth";

pub struct ContextUser {
  id: u32,
}

pub fn auth() -> impl Filter<Extract = (ContextUser,), Error = warp::Rejection> + Clone {
  warp::any()
    .and(warp::header::<String>(XAUTH))
    .and_then(|auth: String| async move {
      if !auth.starts_with("ok:") {
        panic!("invalid auth");
      }
      if let Some(id) = auth.split(":").skip(1).next().and_then(|v| v.parse::<u32>().ok()) {
        Ok::<ContextUser, warp::Rejection>(ContextUser { id })
      } else {
        panic!("invalid auth, need number");
      }
    })
}

main.rs

js
mod header_handler;
use header_handler::{auth, ContextUser};

async fn rest_create(val: Value, user: ContextUser) -> Result<Json, warp::Rejection> {
    let some_thing_warp = warp::reply::json(&val);
    Ok(some_thing_warp)
}

let create = rest_base
  .and(warp::post())
  .and(warp::body::json())
  .and(auth())
  .and_then(rest_create);

4.2.2、登录授权完整示例

header_handler.rs

js
use warp::Filter;

const XAUTH: &str = "x-auth";

pub struct ContextUser {
  id: u32,
}

#[derive(Debug)]
pub struct AuthError;

impl warp::reject::Reject for AuthError {}

#[derive(Debug)]
pub struct AuthErrorNeedsId;

impl warp::reject::Reject for AuthErrorNeedsId {}

pub fn auth() -> impl Filter<Extract = (ContextUser,), Error = warp::Rejection> + Clone {
  warp::any()
    .and(warp::header::<String>(XAUTH))
    .and_then(|auth: String| async move {
      if !auth.starts_with("ok:") {
        return Err(warp::reject::custom(AuthError));
      }
      if let Some(id) = auth.split(":").skip(1).next().and_then(|v| v.parse::<u32>().ok()) {
        Ok::<ContextUser, warp::Rejection>(ContextUser { id })
      } else {
        return Err(warp::reject::custom(AuthErrorNeedsId));
      }
    })
}

main.rs

js
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::{collections::HashMap, convert::Infallible, time::Duration};
use warp::{reply::Json, Filter};

mod header_handler;
use header_handler::{auth, ContextUser};

const WEB_DIR: &str = "public";

async fn rest_get(id: u32) -> Result<Json, warp::Rejection> {
    let some_thing = json!({
      "id": id,
      "name": format!("warp{}", id),
    });
    let some_thing_warp = warp::reply::json(&some_thing);
    Ok(some_thing_warp)
}

async fn rest_list() -> Result<Json, warp::Rejection> {
    let res = json!([
      {
        "id": 1,
        "name": "warp1",
      },
      {
        "id": 2,
        "name": "warp2",
      },
    ]);
    let some_thing_warp = warp::reply::json(&res);
    Ok(some_thing_warp)
}

async fn rest_create(val: Value, user: ContextUser) -> Result<Json, warp::Rejection> {
    let some_thing_warp = warp::reply::json(&val);
    Ok(some_thing_warp)
}

fn rest_api() -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
    let rest_base = warp::path("rest");
    let get = rest_base
        .and(warp::get())
        .and(warp::path::param())
        .and_then(rest_get);
    let list = rest_base.and(warp::get()).and_then(rest_list);
    let create = rest_base
        .and(warp::post())
        .and(warp::body::json())
        .and(auth())
        .and_then(rest_create);
    get.or(list).or(create)
}

#[tokio::main]
async fn main() {
    std::env::set_var("RUST_LOG", "debug");
    pretty_env_logger::init_custom_env("RUST_LOG");

    let index = warp::fs::dir(WEB_DIR);

    let routes = index.or(rest_api());
    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}

4.2.3、线程池

js
let db_pool = Arc::new(DbPool{});
let routes = rest_api(db_pool.clone());

struct DbPool {}

fn with_pool(pool: Arc<DbPool>) -> impl Filter<Extract = (Arc<DbPool>,), Error = Infallible> + Clone  {
  warp::any().map(move || pool.clone())
}

fn rest_api(db_pool: Arc<DbPool>) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
    let rest_base = warp::path("rest");
    let get = rest_base
        .and(warp::get())
        .and(warp::path::param())
        .and(with_pool(db_pool))
        .and_then(rest_get);
    get
}

4.2.4、

Released under the MIT License.