Rust 如何通过迭代路由向量在Rouille中设置路由

Rust 如何通过迭代路由向量在Rouille中设置路由,rust,Rust,显示如何使用路由器宏 下面的示例代码说明了需要能够从数据库或可插拔代码“引导”路由,我目前已经能够使用Iron web框架实现这一点: pub struct Route { pub http_method: String, pub url_path: String, pub callback_func: fn(_: &mut Request) -> IronResult<Response>, } impl Route { pub fn

显示如何使用
路由器

下面的示例代码说明了需要能够从数据库或可插拔代码“引导”路由,我目前已经能够使用Iron web框架实现这一点:

pub struct Route {
    pub http_method: String,
    pub url_path: String,
    pub callback_func: fn(_: &mut Request) -> IronResult<Response>,
}

impl Route {
    pub fn new(m: String, u: String, f: fn(_: &mut Request) -> IronResult<Response>) -> Route {
        Route {
            http_method: m,
            url_path: u,
            callback_func: f,
        }
    }
}

fn main() {
    // router is an Iron middleware
    let mut router = Router::new();

    // prepare routes for bootstrapping into the Iron router
    let r1 = Route::new("get".to_string(), "/*".to_string(), my_callback_func);
    let r2 = Route::new("get".to_string(), "/".to_string(), my_callback_func);

    let mut routes = Vec::new();
    routes.push(r1);
    routes.push(r2);

    for route in routes.iter_mut() {
        if route.http_method == "get" {
            // passes each route to the Iron router
            router.get(&route.url_path, (&*route).callback_func);
        } // else if, put, post, delete...
    }

    Iron::new(router).http("localhost:3000").unwrap();
}

fn my_callback_func(_: &mut Request) -> IronResult<Response> {
    //...
}
pub结构路由{
发布http_方法:字符串,
发布url_路径:字符串,
发布回调函数:fn(&&mut请求)->IronResult,
}
impl路线{
pub-fn-new(m:String,u:String,f:fn(:&mut-Request)->IronResult)->Route{
路线{
http_方法:m,
url_路径:u,
回调函数:f,
}
}
}
fn main(){
//路由器是一个铁中间件
让mut router=router::new();
//准备引导到Iron路由器的路由
让r1=Route::new(“get.to_string(),“/*”。to_string(),my_callback_func);
让r2=Route::new(“get.to_string(),“/”。to_string(),my_callback_func);
让mut routes=Vec::new();
推送(r1);
推送(r2);
用于routes.iter_mut()中的路由{
如果route.http_方法==“get”{
//将每条路线传递到熨斗路由器
router.get(&route.url_path,(&*route.callback_func);
}//否则,如果,放置,发布,删除。。。
}
Iron::new(router).http(“localhost:3000”).unwrap();
}
fn我的回调函数(&&mut请求)->IronResult{
//...
}

虽然我正在阅读Rust中的宏,但我对Rust或一般的宏没有足够的理解,无法找出如何实现与Rouille的等效性。

如果您仔细研究,它很长,但并不十分复杂:

($request:expr, $(($method:ident) ($($pat:tt)+) => $value:block,)* _ => $def:expr) => {
    {
        let request = &$request;

        // ignoring the GET parameters (everything after `?`)
        let request_url = request.url();
        let request_url = {
            let pos = request_url.find('?').unwrap_or(request_url.len());
            &request_url[..pos]
        };

        let mut ret = None;

        $({
            if ret.is_none() && request.method() == stringify!($method) {
                ret = router!(__check_pattern request_url $value $($pat)+);
            }
        })+

        if let Some(ret) = ret {
            ret
        } else {
            $def
        }
    }
};
换句话说,它需要一个请求、零个或多个模式匹配以及一个默认值。它获取URL,然后分派给宏的其他分支,以查看URL是否与路径匹配,并使用路径的组件递归地定义一些变量。无论哪个arm首先匹配,都将设置返回值,如果没有匹配,将使用默认值

不幸的是,宏要求方法和路径使用
ident
s,因此基本上不能将其用于表达式。这意味着我们不能像
“foo”
那样传入变量或文本。这对你来说非常困难

所以,我们做所有优秀程序员都做的事情:复制和粘贴代码。从宏中提取块并重新调整其用途:

#[macro_use]
extern crate rouille;

use rouille::Request;
use rouille::Response;

struct Route(&'static str, &'static str, fn(&Request) -> Response);

fn main() {
    let routes = [
        Route("GET", "/one", do_one),
        Route("GET", "/two", do_two),
    ];

    rouille::start_server("0.0.0.0:9080", move |request| {
        let mut result = None;

        let request = &request;

        // ignoring the GET parameters (everything after `?`)
        let request_url = request.url();
        let request_url = {
            let pos = request_url.find('?').unwrap_or(request_url.len());
            &request_url[..pos]
        };

        for &Route(method, path, f) in &routes {
            if result.is_none() {
                // This checking of the path is terrible, limited, and hacky
                if request.method() == method && request_url.ends_with(path) {
                    result = Some(f(request));
                }
            }
        }

        result.unwrap_or_else(|| Response::text("Default!"))
    });
}

fn do_one(_: &Request) -> Response {
    Response::text("hello world one")
}

fn do_two(_: &Request) -> Response {
    Response::text("hello world two")
}
这将为
/one
/two
和其他所有内容运行各种处理程序


我不是Rouille方面的专家,事实上我在今天之前从未使用过它,但看起来你肯定是在试图用它来做一些超出当前设计目的的事情。也许这是故意的,作者试图提出一个非常固执己见的工具。也许这是偶然的,他们还没有想到这个用例。也许这只是暂时的,他们只是还没来得及去做

不管怎样,我建议问问作者。如果它不是一个预期的用例,他们可以更新项目文档以清楚地说明这一点。否则,他们可能会在实现该功能时产生问题,而您可以帮助设计该功能。

如果您仔细研究,它很长,但并不十分复杂:

($request:expr, $(($method:ident) ($($pat:tt)+) => $value:block,)* _ => $def:expr) => {
    {
        let request = &$request;

        // ignoring the GET parameters (everything after `?`)
        let request_url = request.url();
        let request_url = {
            let pos = request_url.find('?').unwrap_or(request_url.len());
            &request_url[..pos]
        };

        let mut ret = None;

        $({
            if ret.is_none() && request.method() == stringify!($method) {
                ret = router!(__check_pattern request_url $value $($pat)+);
            }
        })+

        if let Some(ret) = ret {
            ret
        } else {
            $def
        }
    }
};
换句话说,它需要一个请求、零个或多个模式匹配以及一个默认值。它获取URL,然后分派给宏的其他分支,以查看URL是否与路径匹配,并使用路径的组件递归地定义一些变量。无论哪个arm首先匹配,都将设置返回值,如果没有匹配,将使用默认值

不幸的是,宏要求方法和路径使用
ident
s,因此基本上不能将其用于表达式。这意味着我们不能像
“foo”
那样传入变量或文本。这对你来说非常困难

所以,我们做所有优秀程序员都做的事情:复制和粘贴代码。从宏中提取块并重新调整其用途:

#[macro_use]
extern crate rouille;

use rouille::Request;
use rouille::Response;

struct Route(&'static str, &'static str, fn(&Request) -> Response);

fn main() {
    let routes = [
        Route("GET", "/one", do_one),
        Route("GET", "/two", do_two),
    ];

    rouille::start_server("0.0.0.0:9080", move |request| {
        let mut result = None;

        let request = &request;

        // ignoring the GET parameters (everything after `?`)
        let request_url = request.url();
        let request_url = {
            let pos = request_url.find('?').unwrap_or(request_url.len());
            &request_url[..pos]
        };

        for &Route(method, path, f) in &routes {
            if result.is_none() {
                // This checking of the path is terrible, limited, and hacky
                if request.method() == method && request_url.ends_with(path) {
                    result = Some(f(request));
                }
            }
        }

        result.unwrap_or_else(|| Response::text("Default!"))
    });
}

fn do_one(_: &Request) -> Response {
    Response::text("hello world one")
}

fn do_two(_: &Request) -> Response {
    Response::text("hello world two")
}
这将为
/one
/two
和其他所有内容运行各种处理程序


我不是Rouille方面的专家,事实上我在今天之前从未使用过它,但看起来你肯定是在试图用它来做一些超出当前设计目的的事情。也许这是故意的,作者试图提出一个非常固执己见的工具。也许这是偶然的,他们还没有想到这个用例。也许这只是暂时的,他们只是还没来得及去做


不管怎样,我建议问问作者。如果它不是一个预期的用例,他们可以更新项目文档以清楚地说明这一点。否则,他们可能会在实现该功能时产生问题,您可以帮助设计该功能。

如果我理解正确:您希望从Iron迁移到Rouille。此外,您希望从数据库中读取数据(这样您就不会在可执行文件中硬编码路由),并且您希望使用Rouille的
路由器宏,但您不知道如何通过数据库中的数据实现这一点。现在:你为什么不注册路由功能而不用
路由器呢宏?例如在[这个例子]中()在
handle_route
fn中,使用db而不是硬接线代码?我不太明白你的意思。handle_route函数既使用router!宏,也使用内部硬编码的路由。我想我可以给出一个粗略的示例来说明我的意思-但在我构思之前:你的用例是什么?它是某种CMS吗?在ro中有uille解释了如何与DBs交互,但它“仅”适用于URL的CRUD请求,如
/note/123
。您非常强调“数据库”一词。这与此无关。它并不总是硬编码您的路由-您可以将它们保存在配置数据库或全局集合中(向量等,如我的简化示例)whic