Rust Web框架warp使用

简介

warp是一个超级便捷、可组合、速度极快的异步Web框架。目前最新版本为v0.2.3,尚未稳定。内部主要基于hyper框架实现,同一个作者,而hyper主要基于tokio。因此hyper的大部分特性都可以在warp中得到了继承:

  • HTTP/1
  • HTTP/2
  • Asynchronous

warp的基本组成部分是filter,他们可以通过组合满足各种不同的需求,个人不太喜欢这种编程风格,warp提供以下开箱即用的功能:

  • Path routing and parameter extraction
  • Header requirements and extraction
  • Query string deserialization
  • JSON and Form bodies
  • Multipart form data
  • Static Files and Directories
  • Websockets
  • Access logging
  • Gzip, Deflate, and Brotli compression

快速开始

  1. 创建项目
cargo new warp-demo
  1. 在cargo.toml中添加依赖
[dependencies]
tokio = { version = "0.2", features = ["macros"] }
warp = "0.2"
  1. 修改main.js
use warp::Filter;

#[tokio::main]
async fn main() {
    // GET /hello/warp => 200 OK with body "Hello, warp!"
    let hello = warp::path!("hello" / String)
        .map(|name| format!("Hello, {}!", name));

    warp::serve(hello)
        .run(([127, 0, 0, 1], 8080))
        .await;
}
  1. 启动
cargo run
  1. 访问
    打开浏览器访问http://localhost:8080/hello/warp

Request和Response

从path和body中获取参数
#![deny(warnings)]

use serde_derive::{Deserialize, Serialize};

use warp::Filter;

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

#[tokio::main]
async fn main() {
    pretty_env_logger::init();

    // POST /employees/:rate  {"name":"Sean","rate":2}
    let promote = warp::post()
        .and(warp::path("employees"))
        .and(warp::path::param::<u32>())
        // Only accept bodies smaller than 16kb...
        .and(warp::body::content_length_limit(1024 * 16))
        .and(warp::body::json())
        .map(|rate, mut employee: Employee| {
            employee.rate = rate;
            warp::reply::json(&employee)
        });

    warp::serve(promote).run(([127, 0, 0, 1], 3030)).await
}
从query中获取参数、设置状态码

cargo.toml

tokio = { version = "0.2", features = ["macros"] }
warp = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0.55"}
log = "0.4" 
pretty_env_logger = "0.3"
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use warp::{
    http::{Response, StatusCode},
    Filter,
};

#[derive(Deserialize, Serialize)]
struct MyObject {
    key1: String,
    key2: u32,
}

#[tokio::main]
async fn main() {
    pretty_env_logger::init();

    // get /example1?key=value
    // demonstrates an optional parameter.
    let example1 = warp::get()
        .and(warp::path("example1"))
        .and(warp::query::<HashMap<String, String>>())
        .map(|p: HashMap<String, String>| match p.get("key") {
            Some(key) => Response::builder().body(format!("key = {}", key)),
            None => Response::builder().body(String::from("No \"key\" param in query.")),
        });

    // get /example2?key1=value,key2=42
    // uses the query string to populate a custom object
    let example2 = warp::get()
        .and(warp::path("example2"))
        .and(warp::query::<MyObject>())
        .map(|p: MyObject| {
            Response::builder().body(format!("key1 = {}, key2 = {}", p.key1, p.key2))
        });

    let opt_query = warp::query::<MyObject>()
        .map(Some)
        .or_else(|_| async { Ok::<(Option<MyObject>,), std::convert::Infallible>((None,)) });

    // get /example3?key1=value,key2=42
    // builds on example2 but adds custom error handling
    let example3 =
        warp::get()
            .and(warp::path("example3"))
            .and(opt_query)
            .map(|p: Option<MyObject>| match p {
                Some(obj) => {
                    Response::builder().body(format!("key1 = {}, key2 = {}", obj.key1, obj.key2))
                }
                None => Response::builder()
                    .status(StatusCode::BAD_REQUEST)
                    .body(String::from("Failed to decode query param.")),
            });

    warp::serve(example1.or(example2).or(example3))
        .run(([127, 0, 0, 1], 3030))
        .await
        }

静态文件、目录

#![deny(warnings)]

use warp::Filter;

#[tokio::main]
async fn main() {
    pretty_env_logger::init();

    let readme = warp::get()
        .and(warp::path::end())
        .and(warp::fs::file("./README.md"));

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

    // GET / => README.md
    // GET /ex/... => ./examples/..
    let routes = readme.or(examples);

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

websocket

#![deny(warnings)]

use futures::{FutureExt, StreamExt};
use warp::Filter;

#[tokio::main]
async fn main() {
    pretty_env_logger::init();

    let routes = warp::path("echo")
        // The `ws()` filter will prepare the Websocket handshake.
        .and(warp::ws())
        .map(|ws: warp::ws::Ws| {
            // And then our closure will be called when it completes...
            ws.on_upgrade(|websocket| {
                // Just echo all messages back...
                let (tx, rx) = websocket.split();
                rx.forward(tx).map(|result| {
                    if let Err(e) = result {
                        eprintln!("websocket error: {:?}", e);
                    }
                })
            })
        });

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

重定向

#![deny(warnings)]
use warp::{http::Uri, Filter};

#[tokio::test]
async fn redirect_uri() {
    let over_there = warp::any().map(|| warp::redirect(Uri::from_static("/over-there")));

    let req = warp::test::request();
    let resp = req.reply(&over_there).await;

    assert_eq!(resp.status(), 301);
    assert_eq!(resp.headers()["location"], "/over-there");
}

tls

#![deny(warnings)]

// Don't copy this `cfg`, it's only needed because this file is within
// the warp repository.
#[cfg(feature = "tls")]
#[tokio::main]
async fn main() {
    use warp::Filter;

    // Match any request and return hello world!
    let routes = warp::any().map(|| "Hello, World!");

    warp::serve(routes)
        .tls()
        .cert_path("examples/tls/cert.pem")
        .key_path("examples/tls/key.rsa")
        .run(([127, 0, 0, 1], 3030))
        .await;
}

#[cfg(not(feature = "tls"))]
fn main() {
    eprintln!("Requires the `tls` feature.");
}
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值