#request #hyper #router #dispatch #regex

match_request

用于在 hyper 中创建请求路由的宏

1 个不稳定版本

0.1.0 2020 年 6 月 13 日

#35 in #dispatch

MIT 许可协议

21KB
301

match_request

用于在 hyper 中创建请求路由的宏。

可以使用更方便易读的语法或使用完整的正则表达式来匹配请求路径。可以在每个路径下列出具体的方法。

匹配的返回值可以是任何东西,尽管你通常会想返回一个包装函数用作视图。

模式语法

匹配请求路径

use match_request::match_request;
use hyper::Method;

let path = "/user/home";
let (value, params) = match_request!(&Method::GET, path, {
    "/login" => {
        GET => "serve login form",
    },
    "/user/home" => {
        GET => "serve home page",
    }
}).unwrap();

assert_eq!(value, "serve home page");

匹配多个方法

use match_request::match_request;
use hyper::Method;

let path = "/example";
let (value, params) = match_request!(&Method::DELETE, path, {
    "/login" => {
        GET => "serve login form",
        POST => "attempt login",
    },
    "/example" => {
        GET => "serve example page",
        DELETE => "delete example page",
    }
}).unwrap();

assert_eq!(value, "delete example page");

命名路径参数

use match_request::match_request;
use hyper::Method;

let path = "/posts/2020/my-blog-post";
let (value, params) = match_request!(&Method::GET, path, {
    "/posts/:year/:slug" => {
        GET => "serve blog post",
    },
}).unwrap();

assert_eq!(params.get("year"), Some("2020"));
assert_eq!(params.get("slug"), Some("my-blog-post"));

捕获路径尾部

use match_request::match_request;
use hyper::Method;

let path = "/static/vendor/img/icon.png";
let (value, params) = match_request!(&Method::GET, path, {
    "/static/*" => {
        GET => "serve static assets",
    },
}).unwrap();

// NOTE: the leading `/` is included
assert_eq!(params.tail(), Some("/vendor/img/icon.png"));

示例路由器

use match_request::{match_request, Error, Params};
use hyper::{Request, Response, Body};
use futures::future::{Future, BoxFuture};

// A boxed type definition for your async views.
type BoxedView = Box<
    dyn Fn(Request<Body>, Params) ->
    BoxFuture<'static, Response<Body>>
>;

// Like a regular `match` expression, `match_request` requires all
// arms to return the same type. As each `async fn` will have a different
// type, you'll likely need to put them in a Box first. This example macro
// is one way to do it.
macro_rules! view {
    ($closure:expr) => {{
        #[allow(unused_mut)]
        let mut closure = $closure;
        let b: BoxedView = Box::new(move |req, params| {
            Box::pin(closure(req, params))
        });
        b
    }};
}

// An example request router.
async fn router(req: Request<Body>) -> Result<Response<Body>, Error> {
    let method = req.method();
    let path = req.uri().path();

    // Attempt to match the request to a view.
    let (handler, params) = match_request!(method, path, {
        "/foo/bar" => {
            GET => view!(foo_bar),
        },
        "/user/:name" => {
            GET => view!(user_profile),
        }
    })?;

    // Execute the view.
    Ok(handler(req, params).await)
}

// Example views...

async fn foo_bar(_req: Request<Body>, _params: Params) -> Response<Body> {
    Response::new(Body::from("Foo bar"))
}

async fn user_profile(_req: Request<Body>, params: Params) -> Response<Body> {
    // Extracting a parameter from the path.
    let name = params.get("name").unwrap();
    Response::new(Body::from(format!("Profile for {}", name)))
}

依赖关系

~12MB
~210K SLoC