什么是Rack的中间?
-
20-09-2019 - |
题
什么是Rack中间,在红宝石?我找不到任何好处解释为什么他们所说的"中间".
解决方案
架作为设计
架件是超过"一个方式过滤器的请求和回应"的执行情况 管道的设计图案 对于网服务器使用 架.
它很干净,分离出来的处理的不同阶段请求分离的问题是一个关键目标的所有精心设计的软件产品。
例如架我可以有单独的阶段的管道这样做:
身份验证:当请求达,用户登录的细节是正确的吗?我怎么验证这个护身份验证,HTTP基本的认证、姓名/密码?
授权:"是的用户被授权执行这个特定任务?", 即基于角色的安全。
缓存:我有没有处理这种请求,我可以返回缓存的结果?
装饰:我如何可以增强的请求下游处理好?
性能和使用的监测:什么样的统计,我可以从中得到的请求和回应?
的执行:实际上处理请求和提供答复。
能够单独的不同阶段(以及有选择地包括他们)是一个很大的帮助在发展中的结构良好的应用。
社会
还有一个很大的生态系统开发周围架中间-你应该可以找到预先建立的架的组成要做到所有上述步骤,并更多。看看 该架。wiki的列表中间.
有什么中间?
中间是一个可怕的术语,是指任何软件组件/图书馆的协助,但是没有直接参与执行的一些任务。很常见的例子是记录、认证和其他 共同点,处理组件的水平.这些往往是事情,每个人都需要在多个应用程序,但没有太多的人感兴趣的(或应该)在建设自己。
更多信息
在评论有关它在被一种过滤器的请求可能是来自 RailsCast集151:架中间 屏幕上投。
架中间演变出架,并有一个很大的介绍在 介绍架中间.
还有一个介绍对中间上的维基百科 在这里,.
其他提示
首先,一架正是两件事情:
- 一个网络服务器接口的公约》
- 一颗宝石
架的网络服务器接口
非常基础的架是一个简单的公约》。每架符合网络服务器将随时打电话通话的方法在一个对象你给他和服务的结果,方法。架指定究竟如何,这一呼吁的方法看起来像什么,它已经返回。那是架。
让我们给它一个简单的尝试。我会使用的选择如架符合网络服务器,但它们中的任何会做的。让我们创建一个简单的网络应用程序,返回JSON串。为此,我们将创建一个文件叫config.ru.的config.ru 将自动被称为由架的宝石的命令rackup这只会运行的内容config.ru 在一架符合网络服务器。因此,让我们添加以下的config.ru 文件:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
map '/hello.json' do
run JSONServer.new
end
作为《公约》规定我们的服务器上有一个方法被称为电话接受环境的散列和返回的一个阵的形式[状态,标题,机构]的网络服务器服务。让我们尝试通过简单调rackup.默认架符合服务器,也许选择的杂种会开始并立即等待请求的服务。
$ rackup
[2012-02-19 22:39:26] INFO WEBrick 1.3.1
[2012-02-19 22:39:26] INFO ruby 1.9.3 (2012-01-17) [x86_64-darwin11.2.0]
[2012-02-19 22:39:26] INFO WEBrick::HTTPServer#start: pid=16121 port=9292
让我们测试我们的新JSON服务器由任何一卷或是url http://localhost:9292/hello.json
瞧:
$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
它的工作。伟大的!这就的基础上为每个网络的框架,它轨道或Sinatra。在某些时候,他们实施一个称方法,工作通过的所有框架码,最后返回响应在典型的[状态,标题,机构]的形式。
在红宝石在轨道上,例如架的请求,命的 ActionDispatch::Routing.Mapper
类,看起来是这样的:
module ActionDispatch
module Routing
class Mapper
...
def initialize(app, constraints, request)
@app, @constraints, @request = app, constraints, request
end
def matches?(env)
req = @request.new(env)
...
return true
end
def call(env)
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
end
...
end
end
所以基本上轨道的检查,依赖于env哈希如果任何路线匹配。如果是这样传递env哈希上的应用以计算的反应,否则就立即回应一个404.因此,任何网络服务器,这是符合该架界面公约,是能够服务于完全吹轨道应用程序。
中间
架还支持建立的中间层。他们基本上拦截的请求,用它做什么,并通过它。这是非常有用的通用任务。
让我们说,我们想要增加登录到我们的JSON服务器,也措施,多长时间的请求采取。我们可以简单地创建一个中间件记录器,不正是这样的:
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
当它得到创建的,这样可以节省本身的副本,实际架应用程序。在我们的情况,是一个实例,我们JSONServer.架自动电话的通话的方法在中间件和期望回来 [status, headers, body]
阵列,就像我们的JSONServer返回。
所以在这中间件,起始点是采取随后的实际调用JSONServer是用 @app.call(env)
, 然后记录器输出的记录条目的最后返回的反应 [@status, @headers, @body]
.
让我们小小的rackup.ru 使用这种中间,加入一个使用RackLogger到这样的:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
use RackLogger
map '/hello.json' do
run JSONServer.new
end
重新启动服务器,瞧,它输出的记录在每一请求。架可以让你加入多个中间,正在以他们加入。这只是一个伟大的方式添加功能而不改变核心的架应用程序。
架的宝石
虽然架首先是一项公约,它还是一个宝石提供了很大的功能。其中一个我们已经用于我们的JSON服务器,rackup命令。但是,还有更多!该架的宝石提供了很少应用于大量使用的情况下,如提供静态的文件,或甚至整目录。让我们看看我们如何服务于一个简单文件,例如一个非常基本HTML文件在位于htmls/index.html:
<!DOCTYPE HTML>
<html>
<head>
<title>The Index</title>
</head>
<body>
<p>Index Page</p>
</body>
</html>
我们也许要提供这种文件的网站根,因此,让我们以下内容添加到我们的config.ru:
map '/' do
run Rack::File.new "htmls/index.html"
end
如果我们参观 http://localhost:9292
我们看到我们html文件的完美呈现。这很容易,对吧?
让我们添加一个整体目录的javascript文件的通过创建一些javascript下的文件/javascript和添加以下的config.ru:
map '/javascripts' do
run Rack::Directory.new "javascripts"
end
重新启动服务器和访问 http://localhost:9292/javascript
你会看到一个列表中的所有javascript文件可以包括现在直接从任何地方。
我了解自己架一个好长的时间的问题。我就使这微型Ruby的web服务器自己的工作后,只有完全理解它。我分享我的学习收获约架(以故事的形式),在这里我的博客:的 http://gauravchande.com/what-is-rack-in-ruby-rails
反馈是更受欢迎。
<强> config.ru
最小可运行示例强>
app = Proc.new do |env|
[
200,
{
'Content-Type' => 'text/plain'
},
["main\n"]
]
end
class Middleware
def initialize(app)
@app = app
end
def call(env)
@status, @headers, @body = @app.call(env)
[@status, @headers, @body << "Middleware\n"]
end
end
use(Middleware)
run(app)
运行rackup
并访问localhost:9292
。输出是:
main
Middleware
因此,清楚的是,Middleware
包裹和调用主应用程序。因此它是能够预先处理该请求,并且后处理以任何方式响应。
于解释: http://guides.rubyonrails.org /rails_on_rack.html#action-dispatcher-middleware-stack ,Rails使用机架中间件对于很多它的功能,你可以添加你自己也有config.middleware.use
家庭的方法。
在中间件实现功能的好处是,你可以重复使用它在任何机架架构,因此所有主要的Ruby的人,而不仅仅是轨。
Rack中间件是过滤的请求,并且响应进入应用程序的方式。中间件组件位于客户机和服务器之间,处理入站请求和出站响应,但比这可以用来跟Web服务器接口的更多。它用于分组和顺序的模块,这通常是Ruby类,以及它们之间的指定相关性。 Rack中间件模块只能: - 具有构造函数的下一个应用在栈参数 - 响应“呼”的方法,即采用环境哈希作为参数。从该调用返回值是数组:状态码,环境的散列和响应主体
什么是Rack?
架提供了一个极小的间接口之间的网络服务器支持红宝石和红宝石框架。
用架你可以写一架应用程序。
架将通过环境哈希(a Hash,包含在HTTP自客户的请求,由类似于CGI headers)到你的架的应用程序,它可以利用的事情载于本哈希做什么它想要的。
什么是一架应用程序?
用架,必须提供一种"应用程序"-一个对象应该 #call
方法与环境的散列作为一个参数(通常定义为 env
). #call
必须返回一系列的三值:
- 的 状态码 (例如'200'),
- 一个 哈希头,
- 的 响应的身体 (它必须应对的红宝石的方法,
each
).
你可以写一架应用程序,返回这一阵列-这样会被送回到你的客户,由架内的一个 响应 (这实际上将是一个 实例 之类的 Rack::Response
[点去docs]).
一个非常简单的架应用程序:
gem install rack
- 创建一个
config.ru
文件架知道看看这个。
我们将创造一个微小的架的应用程序,返回应(一个实例 Rack::Response
人)的答复的身体是一系列包含一串: "Hello, World!"
.
我们将启动一个本地服务器使用的命令 rackup
.
在访问相关的港口,在我们的浏览器,我们将看到"hello World!"呈现在视区。
#./message_app.rb
class MessageApp
def call(env)
[200, {}, ['Hello, World!']]
end
end
#./config.ru
require_relative './message_app'
run MessageApp.new
消防局的服务器 rackup
和访问 localhost:9292 你应该看到'hello World!'呈现。
这不是一个全面的解释,但基本上会发生什么情况是这里的客户(浏览器)发送HTTP请求架,通过你的本地服务器,并且架实例 MessageApp
并运行 call
, ,通过在环境中的散列作为一个参数纳入的方法(《 env
个参数)。
架需要返回值(阵列),并用它来创建一个实例 Rack::Response
并送回到客户。浏览器的使用 魔术 印'hello World!'到屏幕上。
顺便说一句,如果您想看到什么样的环境哈希看起来很像,只是把 puts env
下 def call(env)
.
最小的,因为它是,你有什么写在这里是一架应用!
使得一架应用程序的互动与进入环境哈希
在我们的小架程序中,我们可以互动的 env
哈希(见 在这里, 欲了解更多有关环境hash).
我们将实施能力的用户输入自己的查询串入网址,因此,这串将存在HTTP请求,封装作价值的一个关键值对环境的散列。
我们的架程序将访问,查询串从环境中的散列,并发送给客户(我们的浏览器,在这种情况下)通过体的响应。
从架子文档在环境中的散列:"QUERY_STRING:该部的请求,网址如下?, 如果有的话。可能是空的,但是始终需要!"
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
现在, rackup
和访问 localhost:9292?hello
(?hello
正在查询string)和你应该看到的'hello'呈现在视区。
架中间
我们将:
- 插入一段架中间件进入我们的代码-一级:
MessageSetter
, - 环境哈会打击这类第一,可以通过在作为一个参数:
env
, MessageSetter
将插入一个'MESSAGE'
关键成env散,它的价值'Hello, World!'
如果env['QUERY_STRING']
是空;env['QUERY_STRING']
如果没有,- 最后,它将返回
@app.call(env)
-@app
在未来的应用程序的'堆':MessageApp
.
第一,长手'version:
#./middleware/message_setter.rb
class MessageSetter
def initialize(app)
@app = app
end
def call(env)
if env['QUERY_STRING'].empty?
env['MESSAGE'] = 'Hello, World!'
else
env['MESSAGE'] = env['QUERY_STRING']
end
@app.call(env)
end
end
#./message_app.rb (same as before)
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
#config.ru
require_relative './message_app'
require_relative './middleware/message_setter'
app = Rack::Builder.new do
use MessageSetter
run MessageApp.new
end
run app
从 机架:建设者文档 我们看到, Rack::Builder
实现了一个小DSL迭代地构架的应用。这基本上意味着你可以建立一个'堆'组成的一个或多个中间和下一级应用来分派。所有请求都经过你的底层应用将首先处理你的中间件(s)。
#use
指定的中间件,用于堆叠。它需要的中间件作为一个参数。
架中件必须:
- 有一个构造,需要未来的应用程序在堆作为一个参数。
- 响应
call
方法,需要环境中的散列作为一个参数。
在我们的情况下,'中间件'是 MessageSetter
, ,'constructor'是MessageSetter的 initialize
方法中,'未来应'在栈 MessageApp
.
因此,这里的原因是什么 Rack::Builder
不会发动机罩下, app
参数 MessageSetter
's initialize
方法是 MessageApp
.
(得到你的头上述之前)
因此,每一件中间件基本上是'通行证下的现有环境中的散列的下一个应链中-那你就有机会改变这种环境中的散列在中间件之前,通过它,未来的应用程序。
#run
需要一个论点,即一个目的是响应 #call
和返回架响应(一个实例 Rack::Response
).
结论
使用 Rack::Builder
你可以建造链的中间的任何请求您的申请会处理,由每个中间件中转之前,最后被处理的最后一块堆(在我们的情况下, MessageApp
).这是非常有用的,因为它分离出的不同阶段处理的请求。在'分离的问题,它不可能多的吸尘器!
你可以构造一个要求管道的'组成的几个中间,处理的事情,例如:
- 身份验证
- 授权
- 缓存
- 装饰
- 性能和使用的监测
- 执行(实际上处理的请求,并提供一个响应)
(上述要点,从另一个答案在这个线)
你会经常看到这种在专业Sinatra应用程序。Sinatra采用齿!看看 在这里, 为什么的定义Sinatra 是!
作为最后说明,我们的 config.ru
可以写一个简短的手风格,生产完全相同的功能(这也是你通常看到的):
require_relative './message_app'
require_relative './middleware/message_setter'
use MessageSetter
run MessageApp.new
并展现出更为明确的是什么 MessageApp
是这样做的,这是其长一方面的版本明确表示, #call
是创建一个新的实例 Rack::Response
, 与所需要的三个参数。
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
有用的链接
架界b/w网和应用程序服务器
架是红宝石的包裹,它提供了一个界面为一个网服务器进行通信应用程序。它是容易加入的中间件成分之间的网络服务器和应用修改的方式你的请求/回应的行为。中间件成分之间的客户和服务,处理入境要求和出反应。
在外行人说,这基本上是一组指导原则如何服务器和一个轨道应用程序(或任何其他宝石的网应用程序)应当彼此交谈.
用架,提供了一个"程序":一个目来响应该呼吁的方法,把环境中的散列作为一个参数,以及返回一系列有三个要素:
- HTTP响应的代码
- 哈希头
- 的 响应的身体, 必须应对每 请求.
为更多的解释,可以按照下面的链接。
1. https://rack.github.io/
2. https://redpanthers.co/rack-middleware/
3. https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware
4. https://guides.rubyonrails.org/rails_on_rack.html#resources
在轨道,我们有config.ru 作为一架文件,可以运行的任何架文件 rackup
命令。和默认的口为这是 9292
.来测试这个,你只可以运行 rackup
在你的轨目录,并看到结果。你也可以指定港口要对其运行。执行的命令架文件在任何特定港口
rackup -p PORT_NUMBER