前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Rust每周一库】Rocket - 流行的网络开发框架

【Rust每周一库】Rocket - 流行的网络开发框架

作者头像
MikeLoveRust
发布2020-02-20 10:24:34
2.4K0
发布2020-02-20 10:24:34
举报

简介

Rocket是一个基于Rust编写的上层网络框架,是目前rust主流的网络框架之一,有8.8k的star。而它的http部分就是基于之前提到的hyper。按官方说法,具有如下三个特点:1安全无误、开发体验好 2自动解决请求处理的类型问题,且无需全局状态 3各种可插拔的可选组件。那让我们来一起看一看吧~

准备工作

需要在Cargo.toml中加入依赖

代码语言:javascript
复制
[dependencies]
rocket = "0.4.2"

然后需要注意,Rocket需要使用nigthly编译。也可以使用以下指令在当前目录中默认使用nightly

代码语言:javascript
复制
rustup override set nightly

Hello World

首先我们来写一个最简单的服务器,向请求返回hello world

代码语言:javascript
复制
// Rocket用到的rust的nightly的特性
#![feature(proc_macro_hygiene, decl_macro)]

use rocket::{get, routes, Rocket};

// get函数hello
#[get("/")]
fn hello() -> &'static str {
    "Hello, world!"
}

fn rocket() -> Rocket {
    // 把hello函数挂载到/
    rocket::ignite().mount("/", routes![hello])
}

fn main() {
    rocket().launch();
}

那大家可能会好奇,为什么hello返回的是一个字符串,Rocket就能把它作为response返回呢? 这是因为Rocket中返回类型需要实现Responder Trait。而一些标准库中的类型已经有了实现,比如String的实现如下

代码语言:javascript
复制
impl Responder<'static> for String {
    fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
        Response::build()
            .header(ContentType::Plain)
            .sized_body(Cursor::new(self))
            .ok()
    }
}

因此我们可以直接返回这些常用类型,而Rocket就能够自动帮我们把他们转化为response。 我们也可以类似的定义自己的类型去实现Responder。

动态分发

如果需要路由中有动态部分,可以使用<>。比如我们现在升级下我们的hello服务器,使得路径中可以有一个动态的名字变量name

代码语言:javascript
复制
#[get("/hello/<name>")]
fn hello(name: String) -> String {
    format!("Hello, {}!", name)
}

也支持路由后的query参数,按照如下格式

代码语言:javascript
复制
#[get("/hello?wave&<name>")]

测试

Rocket本身提供了本地的客户端,可以方便对服务器进行测试。比如之前我们写过hello服务器升级版,就可以很容易的进行测试

代码语言:javascript
复制
#[cfg(test)]
mod test {
    use super::rocket;
    use rocket::local::Client;
    use rocket::http::Status;

    #[test]
    fn hello() {
        let client = Client::new(rocket()).expect("valid rocket instance");
        let mut response = client.get("/hello/john").dispatch();
        assert_eq!(response.status(), Status::Ok);
        assert_eq!(response.body_string(), Some("Hello, john!".into()));
    }
}

中间件

Rocket中相当于中间件的,有Request Guard和Fairing。前者可以用来处理权限管理等逻辑,而后者主要用来加入全局的Hook。 先来一个Guard的例子,这里是Rocket内置的Cookie。完整的代码点这里

代码语言:javascript
复制
// 这个例子也可以看到Rocket对表格的友好支持
#[derive(FromForm)]
struct Message {
    message: String,
}

// 表格中的信息被加入Cookie
#[post("/submit", data = "<message>")]
fn submit(mut cookies: Cookies, message: Form<Message>) -> Redirect {
    cookies.add(Cookie::new("message", message.into_inner().message));
    Redirect::to("/")
}

// 读取Cookie的内容
#[get("/")]
fn index(cookies: Cookies) -> Template {
    let cookie = cookies.get("message");
    let mut context = HashMap::new();
    if let Some(ref cookie) = cookie {
        context.insert("message", cookie.value());
    }

    Template::render("index", &context)
}

注意,Request Guard的一般形式是

代码语言:javascript
复制
#[get("/<param>")]
fn index(param: isize, a: A, b: B, c: C) -> ... { ... }

其中a,b,c这些不在参数列表的就是Request Guard了,需要实现FromRequest Trait。

而下面的例子则是一个Fairing,用来给GET和POST请求加上一个计数器(Fairing一共可以有on_attach, on_launch, on_request和on_response这四个Hook,on_attach是在被attach到Rocket实例的时候,on_launch是Rocket实例启动的时候,后面两个就是字面意思啦)

代码语言:javascript
复制
#[derive(Default)]
struct Counter {
    get: AtomicUsize,
    post: AtomicUsize,
}

impl Fairing for Counter {
    fn info(&self) -> Info {
        Info {
            name: "GET/POST Counter",
            kind: Kind::Request | Kind::Response
        }
    }

    fn on_request(&self, request: &mut Request, _: &Data) {
        if request.method() == Method::Get {
            self.get.fetch_add(1, Ordering::Relaxed);
        } else if request.method() == Method::Post {
            self.post.fetch_add(1, Ordering::Relaxed);
        }
    }

    fn on_response(&self, request: &Request, response: &mut Response) {
        // Don't change a successful user's response, ever.
        if response.status() != Status::NotFound {
            return
        }

        if request.method() == Method::Get && request.uri().path() == "/counts" {
            let get_count = self.get.load(Ordering::Relaxed);
            let post_count = self.post.load(Ordering::Relaxed);

            let body = format!("Get: {}\nPost: {}", get_count, post_count);
            response.set_status(Status::Ok);
            response.set_header(ContentType::Plain);
            response.set_sized_body(Cursor::new(body));
        }
    }
}

配置文件

一般会在运行的根目录下放置Rocket.toml,配置Rocket在development,staging和production环境中的参数,比如服务器地址端口,请求限制,worker线程数量等。下面是一个示例:

代码语言:javascript
复制
[development]
address = "localhost"
port = 8000
limits = { forms = 32768 }

[production]
address = "0.0.0.0"
port = 8000
workers = [number of cpus * 2]
keep_alive = 5
log = "critical"
secret_key = [randomly generated at launch]
limits = { forms = 32768 }

小结

其实Rocket还有很多可以讲的内容。限于篇幅就讲到这里啦。大家有兴趣的可以自己去阅读官方的例子和教程,并在实践中学习吧~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust语言学习交流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 准备工作
  • Hello World
  • 动态分发
  • 测试
  • 中间件
  • 配置文件
  • 小结
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档