什么是 Rack?

本文章同步发布于 我的部落格

也欢迎关注我的 Facebook 以及 Instagram 接收软体相关的资讯!

什么是 Rack ?

Rack 是 Ruby 所有的网路框架背后最底层的技术,Rack 可以是:

1。 一个架构:Rack 定义了一个非常简单的结构,任何符合这个结构的程式码都可以在 Rack 的应用程式中被应用,这让我们在建构小型、集中、可重複使用的程式码段落变得简单,然后组合这些 Rack 变成一个更大的应用程式。

2。一个 Ruby Gem:以 Ruby Gem 的形式发布,并且提供了组合程式码所需要的 glue code。

Rack Interface

Rack 定义了一个很简单的 interface 以及接口,符合 Rack 标準的程式码包含下面三点:

1。他必须对 call 做出回应,call 方法必须接受一个参数,通常是 env 或是 environment,因为他会汇集所有关于这次请求的资料,就像是这次请求送过来的所有参数。

2。call 方法必须回应一个由 HTTP状态码 & Headers 以及 body(内容) 所组成的阵列

3。关于 call 有个很棒的点就是 Proc 以及 lambda 可以被当作 Rack 的物件做使用

Rack Hello World

接下来的例子是一个符合 Rack 标準的 app,简单地回应 Hello World

require 'rack'class HelloWorld  def call(env)    [ 200, { "Content-Type" => "text/plain" }, ["Hello World"] ]  endendrun HelloWorld.new

在上面的例子里面,我们实践了一个类别搭配一个实体方法 call,并且加入环境变数,然后总是回传 HTTP Status 200、一个 Content-type 的 headers,最后是内容 Hello World

还有一个需要注意的地方,Rack 期待 body 回传的部分是能够套用 each 方法,所以单纯的字串需要被放到阵列里面才能够正确的回应,但 body 大部分都是其他类型的 IO 物件,而不是单纯的字串,只要不是单纯字串,都可以不需要套入阵列之中。

探索 env 物件

require 'rack'class HelloWorld  def call(env)    [ 200, { "Content-Type" => "text/plain" }, [env.inspect] ]  endend

将原本的程式码改成上面的样子,就可以看到 env 物件本身是一个 hash,然后内部集合了所有关于这次 request 和 response 的内容。

里面包含了使用者使用的装置,cookies 以及 request 的路径等等,全部的值都是简单的字串,而不是像 rails 一样複杂结构的 hash,但是所有的东西都在这包 hash 内,也供我们能够检查和组成我们的伺服器端的回应!

使用 lambda

这个示範只是好玩,并证明上面提到可以使用 lambda 或是 Proc 来执行 rack!

require 'rack'app = -> (env) do  [200, { 'Content-type' => 'text/plain'}, [env.inspect]]endrun app

Middleware

Middleware 是用 rack 模式所组成的,通常会是大型应用程式的小积木,每一个 middleware 都是一个和 rack 相容的程式,而最终的应用程式就是透过这些 middleware 组合再一起而成。

Logging Middleware 实作

不像一般的 rack 程式,middleware 必须是类别,因为需要 initialize 来存放 app 并且传送给执行链上的下一个 middleware 或是 rack。

像是下面的 middleware 示範,我们在 request 以及 response 中间加入了 logs 来计算回应所花费的时间。

但在一开始,我们需要让 Rack app 先沈睡三秒,这样我们才有东西可以计算,来看看怎么写吧!

require 'rack'app = ->(env) do  sleep 3  [200, {'Content-type' => 'text/plain'}, ['Hello, World']]endclass LoggingMiddleware  def initialize(app)    @app = app  end  def call(env)    before = Time.now.to_i    status, headers, body = @app.call(env)    after = Time.now.to_i    log_messages = "App Work #{after - before} second."    [status, headers, [log_messages.inspect]]  endendrun LoggingMiddleware.new(app)

这里我们可以看到,我们交给 LoggingMiddlewareapp 是最基础的应用程式,被我们的 middleware 给包覆住了。

在 middleware 的 call 方法中,我们撷取了时间戳记,并把请求转送给执行链上的下一个 app,每一个 middleware 都只知道下一个是谁,所以在最终的结果出来之前,可能有超级多层的 middleware。

在我们 call 下一个 app 的那一行,我们解构了 app 回传的阵列到 status, headers, body,这样可以让我们针对回传的结果做检查或是更改。

在这个 LoggingMiddleware 的情况下,我们把时间戳记加入到了回应的 body 内,并且让其显示在画面中。

Middleware 的实际用法

感谢 Rack interface 的简单性和灵活性,middleware 被用在大量不同的功能。

有一个很酷的东西叫做 rack-honeypot 可以拿来侦测和应用程式互动的垃圾邮件或是恶意机器人,他在所有的 response 中加入了一个隐藏的栏位,是只有机器人才有可能填写的,然后他会解析这个所有的请求,并取消掉有填了隐藏栏位的请求。

Rack::Builder

虽然 Rack 和使用 middleware 是一个很好把应用程式组合起来的方式,但他可能会变得有点冗长,而 Rack::Builder 是一个模式,可以让我们更简洁地定义我们的应用程式和 middleware

我们可以在 config.ru 中定义,这是一个 Rack 专用的文件 ( .ru 代表 rackup )

app = ->(env) do  sleep 3  [200, {'Content-type' => 'text/plain'}, ['Hello, World']]endclass LoggingMiddleware  def initialize(app)    @app = app  end  def call(env)    before = Time.now.to_i    status, headers, body = @app.call(env)    after = Time.now.to_i    log_messages = "App Work #{after - before} second."    [status, headers, [log_messages.inspect]]  endenduse LoggingMiddlewarerun app

主要的变化就是可以透过 run & use 来建构我们的应用程序,可以一目了然地知道使用了什么样子的 middleware

The middleware stack

上面的 Rack::Builder 方法可以让我们更轻鬆地建构应用程式的 middleware stack,middleware 将会已特定地顺序排序,并以特定地顺序执行。

这边就是所谓的 FILO ( First In Last Out ),所以执行的顺序会先是 run 的那个主程式,因为他是最后放进 stack 里面的,接着再往上执行( 就是往程式码的上放继续执行下去 )

一个 request 将会穿越这个 middleware stack 来到最终的 app,然后所有的 response 都会透过执行链向上传递。

use FirstMiddleware #第四个执行use SecondMiddleware #第三个执行use ThirdMiddleware #第二个执行run app #第一个执行

用 rackup 跑起来!

当我们有了 config.ru 来组织我们的 middleware,我们就可以利用 rack gem 提供的 rackup 来执行我们的 rack app

rackup 执行的时候,将会自动载入 rack,并且 use & run 方法将会被使用。

我们可以用 Middleware 做些什么?

Middleware 很适合拿来用在非应用程式的特定逻辑。像是设置暂存的 headers,logging,解析 request 物件等等...

上述提到的几个用法,都是在 Rails 里面很常见的 middleware!

结语

Rack 本身提供了强大又简洁的抽象概念,让我们可以去分离应用程式的核心逻辑和外围的关注点 ( ex: 解析 HTTP headers )

他几乎是所有 Ruby 有关的网路框架拿来运行和 server 之间的 interface,真的是非常好用!

会想写关于 Rack 的东西是因为,今天被有被问到关于 Rack 的问题,但觉得自己解释的并没有很好,所以才赶快来研究并且纪录!


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章