• Github 中文镜像
Sign inSign up
Watch966
Star102.4k
Fork61.8k
Tag: rust
Switch branches/tags
K / Tide 2019-11-27.md
移动浏览 Clone
加载中...
到移动设备上浏览
159 lines 13.50 KB
First commit on 8 Jun 2020

    原文:https://blog.yoshuawuyts.com/tide/

    机翻、简单润色。

    • 北极星
    • Tide 的今天
    • Tide 的体系结构
    • 下一步
    • 结论

    今天我们很高兴地宣布 Tide 0.4.0 的发布具有令人兴奋的新设计。 这篇文章将(简要地)介绍 Tide 周围以及我们前往的一些激动人心的进展。

    北极星

    三周前,async / .await MVP 作为 Rust 1.39 的一部分发布。 我们还有很长的路要走,并且诸如异步特征,异步迭代语法和异步闭包之类的功能意味着我们将来将要编写的异步代码可能与今天的工作方式有所不同。

    这就是为什么我们希望从共享中开始,以便将来将Tide带到哪里,但不能这么做,因为我们受到时代技术的限制。 我们希望使 Tide 成为一种快速的,基于请求响应的流式 Web 框架。 在典型的 Rust 精神中:在人体工程学和性能之间不妥协。

    Reply with “hello world”:

    async fn main() -> tide::Result<()> {
        let mut app = tide::new();
        app.at("/").get(|_| "Hello, world!");
        app.listen("127.0.0.1:8080").await
    }
    
    Serialize + Deserialize JSON:
    
    #[derive(Deserialize, Serialize)]
    struct Cat {
        name: String
    }
    
    async fn main() -> tide::Result<()> {
        let mut app = tide::new();
        app.at("/submit").post(async |req| {
            let cat: Cat = req.body_json()?;
            println!("cat name: {}", cat.name);
    
            let cat = Cat { name: "chashu".into() };
            tide::Response::new(200).body_json(cat)
        });
        app.listen("127.0.0.1:8080").await
    }
    

    这两个示例都非常简短,但是在功能方面却做了很多。 我们认为使用异步 Rust 应该像同步 Rust 一样容易,并且随着语言功能的发展,这将越来越成为现实。

    Tide 的今天

    就像我们说的,我们还没有到那儿。 今天,我们发布了Tide 0.4.0,这是朝着这个方向迈出的第一步。 我们的“ hello world”如下所示:

    #[async_std::main]
    async fn main() -> io::Result<()> {
        let mut app = tide::new();
        app.at("/").get(|_| async move { "Hello, world!" });
        app.listen("127.0.0.1:8080").await
    }
    

    注意到 get 处理程序中额外的异步 move {} 了吗? 那是因为异步闭包还不存在,这意味着我们需要 block 语句。 但是也没有 regular closures 的完全实现(blanket impls)。

    #[derive(Deserialize, Serialize)]
    struct Cat {
        name: String,
    }
    
    #[async_std::main]
    async fn main() -> io::Result<()> {
        let mut app = tide::new();
    
        app.at("/submit").post(|mut req: tide::Request<()>| async move {
            let cat: Cat = req.body_json().await.unwrap();
            println!("cat name: {}", cat.name);
    
            let cat = Cat {
                name: "chashu".into(),
            };
            tide::Response::new(200).body_json(&cat).unwrap()
        });
    
        app.listen("127.0.0.1:8080").await
    }
    

    JSON示例同样还有一段路要走。特别是错误处理可能确实需要一些工作。注意到包装了吗? 是的,不是很好。要解决此问题,它在我们的待办事项列表中很高。总的来说,还有一些不优雅,但我们绝对是正确的。

    Tide 结构

    Request-Response

    与之前的 Tide 版本相比,一个很大的变化是我们现在直接基于 Request-Response 模型。这意味着请求进入,并且返回响应。这听起来似乎很明显,但是例如,Node.js 使用 res.end 回调发送回响应,而不是从函数返回响应。

    async fn(req: Request) -> Result<Response>;
    

    中间件

    除了请求和响应,Tide 还允许传递中间件,全局状态和本地状态。中间件包装每个请求和响应对,从而允许代码在端点(endpoint)之前和每个端点之后运行。此外,每个处理程序可以选择永不屈服于端点并提早中止。这对于例如身份验证中间件。

    Tide 0.4.0 随附了一个基于 log crate 的请求记录器。 该中间件将在每个请求进入时记录每个请求,并在其离开时记录每个响应。

    use tide::middleware::RequestLogger;
    
    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let mut app = tide::new();
        app.middleware(RequestLogger::new());
        app.at("/").get(|_| async move { "Hello, world!" });
        app.listen("127.0.0.1:8080").await
    }
    

    Tide 中间件像堆栈一样工作。 记录器中间件的简化示例如下:

    async fn log(req: Request, next: Next) -> Result<Response> {
        println!("Incoming request from {} on url {}", req.peer_addr(), req.url());
        let res = next().await?;
        println!("Outgoin response with status {}", res.status());
        res
    }
    

    随着新请求的到来,我们执行一些逻辑。 然后,我们屈服于下一个中间件(或者端点,我们不知道何时屈服于下一个中间件),一旦完成,我们就返回响应。我们可以决定在任何阶段都不屈服于下一个阶段,并尽早放弃。

    中间件的运行顺序为:

         Tide
    1.          7.  Middleware 1
    ==============
    2.          6.  Middleware 2
    ==============
    3.          5.  Middleware 3
    ==============
          4.        Endpoint
    

    状态(State)

    中间件通常需要与端点共享值。 这是通过“本地状态”完成的。使用通过 Request::local_state 可用的 typemap 构建本地状态。

    当完整的应用程序需要访问特定值时,将使用全局状态。这样的示例包括:数据库连接,websocket 连接或启用网络的配置。 每个 Request <State> 具有一个内部值,该值必须实现 Send + Sync + Clone,因此可以在请求之间自由共享。

    默认情况下,tide::new 将使用 () 作为共享状态。但是,如果您要创建具有共享状态的新应用,则可以执行以下操作:

    /// Shared state
    struct MyState {
        db_port: u16,
    }
    
    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let state = State { db_port: 8083 };
        let mut app = tide::with_state(state);
        app.at("/").get(|_| async move { "Hello, world!" });
        app.listen("127.0.0.1:8080").await
    }
    

    Extension Traits

    有时,具有全局和局部上下文可能需要一些设置。在某些情况下,如果事情变得简单一点会很好。这就是为什么潮汐鼓励人们写延伸特征的原因。

    通过使用扩展特征,您可以使用更多功能扩展请求或响应。例如,身份验证包可以在 Request 上实现用户方法,以访问由中间件提供的经过身份验证的用户。 或者,GraphQL 包可以将 body_graphql 方法实现为 RequestResponse ,与 body_json 对应,从而使 GraphQL 的序列化和反序列化变得更加容易。

    甚至更有趣的是全局状态,派生和扩展特性之间的相互作用。可能存在一个与ORM相邻的扩展世界。也许还有更多我们没有想到的; 但我们鼓励您尝试并分享自己的想法。

    基本形式的扩展特征如下所示:

    pub trait RequestExt {
        pub fn bark(&self) -> String;
    }
    
    impl<State> RequestExt for Request<State> {
        pub fn bark(&self) -> String {
            "woof".to_string()
        }
    }
    

    Tide apps 随后将可以根据 Request 访问 bark 方法。

    #[async_std::main]
    async fn main() -> Result<(), std::io::Error> {
        let mut app = tide::new();
        app.at("/").get(|req| async move { req.bark() });
        app.listen("127.0.0.1:8080").await
    }
    

    下一步?

    从我们的 JSON 示例可以看出,错误处理还不是很好。 错误类型与我们希望的错误类型不一致,这是一个问题。 在我们的清单中,去除使潮汐功能正常运行所需的展开物很高。

    但是之后,我们想集中精力扩展功能集。 人们想使用 Web 应用程序做很多事情,我们想了解它们是什么。 特别是,我们经常听到 WebSockets 经常出现。 但是,开箱即用地实现良好的 HTTP 安全性也是如此。

    潮汐仍处于初期,我们为人们将要建造的东西感到兴奋。 我们希望知道您使用 Tide 的经历。 我们越了解人们在做什么,就越能使 Tide 成为帮助人们成功的工具。

    结论

    在本文中,我们介绍了 Tide 的未来和现在,并介绍了其架构和设计理念。 可能需要重复一遍,我们的 0.4.0 版本几乎无法反映完成状态。 相反,这是迈向该项目新方向的第一步。 对于 Rust 的未来,尤其是异步网络,我们感到非常兴奋。

    我们相信 Tide 为编写 HTTP 服务器提出了一个有趣的方向。 一种将其他语言的熟悉度与 Rust 独特的做事方式相结合的方式,所产生的不仅仅是其部分的总和。 无论哪种方式; 我们很高兴与大家分享这一点。 因此,我要放假了——回到 12 月 11 日。 我们希望您喜欢 Tide 0.4!


    感谢 Friedel Ziegelmayer,Felipe Sere,Tirr-c,Nemo157,Oli Obk,David Tolnay 以及其他无数人在邮件,问题,错误修复,设计以及发布所涉及的所有其他工作方面的帮助和协助。没有每个参与的人,Tide 将不可能实现。