Ruby中的机架中间件是什么?对于他们所说的“中间件”,我找不到任何好的解释。
当前回答
机架- Web和应用服务器的接口
Rack是一个Ruby包,它为web服务器提供了与应用程序通信的接口。在web服务器和应用程序之间添加中间件组件来修改请求/响应的行为方式是很容易的。中间件位于客户机和服务器之间,处理入站请求和出站响应。
通俗地说,它基本上只是一组指导方针,说明服务器和Rails应用程序(或任何其他Ruby web应用程序)应该如何相互通信。
要使用Rack,提供一个“app”:一个响应调用方法的对象,将环境哈希作为参数,并返回一个包含三个元素的数组:
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
在rails中,我们将config.ru作为一个机架文件,您可以使用rackup命令运行任何机架文件。默认端口是9292。要测试这一点,只需在rails目录中运行rackup并查看结果。您还可以指定要在哪个端口上运行它。在任何特定端口上运行机架文件的命令为
rackup -p PORT_NUMBER
其他回答
我使用Rack中间件解决了几个问题:
使用自定义机架中间件捕获JSON解析错误,并在客户端提交错误JSON时返回格式化良好的错误消息 内容压缩通过机架::Deflater
在这两种情况下,它都提供了相当优雅的修复。
Rack middleware is a way to filter a request and response coming into your application. A middleware component sits between the client and the server, processing inbound requests and outbound responses, but it's more than interface that can be used to talk to web server. It’s used to group and order modules, which are usually Ruby classes, and specify dependency between them. Rack middleware module must only: – have constructor that takes next application in stack as parameter – respond to “call” method, that takes environment hash as a parameter. Returning value from this call is an array of: status code, environment hash and response body.
Rack提供了一个简单的接口来抽象HTTP请求/响应。Rack位于web框架(Rails, Sinatra等)和web服务器(unicorn, puma)之间,作为适配器。从上图来看,这使得unicorn服务器完全独立于了解rails的服务器,而rails不知道unicorn的服务器。这是一个松耦合、关注点分离的好例子。
上图来自机架https://youtu.be/3PnUV9QzB0g上的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
因此很明显,中间件包装并调用主应用程序。因此,它能够以任何方式预处理请求和后处理响应。
正如在http://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack中解释的那样,Rails使用Rack中间件来实现它的许多功能,你也可以使用config.middleware.use家族方法添加你自己的中间件。
在中间件中实现功能的好处是,您可以在任何机架框架上重用它,因此可以重用所有主要的Ruby框架,而不仅仅是Rails。
机架是什么?
Rack提供了支持Ruby和Ruby框架的web服务器之间的最小接口。
使用Rack可以编写一个Rack应用程序。
机架将传递环境哈希(一个哈希,包含在来自客户端的HTTP请求中,由类似cgi的头组成)给您的机架应用程序,该应用程序可以使用这个哈希中包含的内容来做任何它想做的事情。
什么是机架应用程序?
要使用Rack,你必须提供一个“app”——一个响应#call方法的对象,并将环境哈希作为参数(通常定义为env)。#call必须返回一个包含三个值的数组:
状态码(例如“200”), 一个头的哈希, 响应体(必须响应Ruby方法)。
你可以写一个Rack应用程序,返回这样一个数组-这将被发送回你的客户端,由机架,在一个响应(这实际上是类Rack::Response的一个实例[点击进入文档])。
一个非常简单的机架应用程序:
Gem安装架 创建一个config.ru文件- Rack知道要寻找这个文件。
我们将创建一个小型的Rack应用程序,它返回一个响应(Rack::Response的一个实例),其响应体是一个包含String: "Hello, World!"的数组。
我们将使用命令机架启动本地服务器。
当在浏览器中访问相关端口时,我们将看到“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获取返回值(数组)并使用它创建Rack::Response的实例并将其发送回客户端。浏览器使用魔法打印“Hello, World!”对着屏幕说。
顺便说一句,如果你想看看环境哈希是什么样的,只要把puts env放在def call(env)下面。
尽管非常简单,但您在这里编写的是一个机架应用程序!
使机架应用程序与传入环境哈希进行交互
在我们的小Rack应用程序中,我们可以与env哈希进行交互(有关Environment哈希的更多信息,请参阅这里)。
我们将实现用户向URL中输入自己的查询字符串的能力,因此,该字符串将出现在HTTP请求中,封装为Environment散列的一个键/值对中的值。
我们的Rack应用程序将从Environment哈希中访问该查询字符串,并通过响应中的Body将其发送回客户端(在本例中是我们的浏览器)。
从机架文档的环境哈希: QUERY_STRING:请求URL中跟在?后面的部分(如果有的话)。可能是空的,但总是需要的!”
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
现在,安装并访问localhost:9292?你好(?Hello是查询字符串),你应该看到' Hello '呈现在视口中。
架的中间件
我们将:
插入一个机架中间件到我们的代码库-一个类:MessageSetter, Environment哈希将首先碰到这个类,并将作为参数传入: MessageSetter将在env散列中插入一个'MESSAGE'键,其值为'Hello, World!' if env['QUERY_STRING']为空;env['QUERY_STRING']如果不是, 最后,它将返回@app.call(env) - @app作为“堆栈”中的下一个应用:MessageApp。
首先,“长期”版本:
#./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文档中,我们看到Rack::Builder实现了一个小的DSL来迭代地构造Rack应用程序。这基本上意味着你可以构建一个“堆栈”,由一个或多个中间件和一个“底层”应用程序组成。所有通过底层应用程序的请求都将首先由中间件处理。
#use指定在堆栈中使用的中间件。它以中间件作为参数。
机架中间件必须:
构造函数将栈中的下一个应用程序作为参数。 响应以Environment散列作为参数的调用方法。
在我们的例子中,“中间件”是MessageSetter,“构造函数”是MessageSetter的初始化方法,堆栈中的“下一个应用程序”是MessageApp。
这里,由于Rack::Builder在底层所做的工作,MessageSetter的initialize方法的app参数是MessageApp。
(在继续前进之前先想想上面的内容)
因此,中间件的每个部分本质上都是将现有的环境哈希传递给链中的下一个应用程序——因此,在将其传递给堆栈中的下一个应用程序之前,您有机会在中间件中改变环境哈希。
#run接受一个参数,该参数是一个响应#call的对象,并返回一个Rack Response (Rack::Response的实例)。
结论
使用Rack::Builder,您可以构建中间件链,对应用程序的任何请求都将由每个中间件依次处理,最后由堆栈中的最后一块处理(在我们的例子中,是MessageApp)。这非常有用,因为它分离了处理请求的不同阶段。就“关注点分离”而言,它再干净不过了!
你可以构建一个“请求管道”,由几个中间件组成,处理如下事情:
身份验证 授权 缓存 装饰 性能和使用监控 执行(实际处理请求并提供响应)
(以上是本帖另一个答案的要点)
您将经常在专业的Sinatra应用程序中看到这一点。Sinatra使用Rack!看这里什么是辛纳屈的定义!
最后要注意的是,我们的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
有用的链接
这篇文章的完整代码(Github回购提交) 好的博文,“机架中间件介绍” 一些好的机架文档
推荐文章
- 确定ActiveRecord对象是否为New
- Rails中的OO设计:在哪里放置东西
- 如何使HTTP请求在PHP和不等待响应
- PATCH和PUT请求的主要区别是什么?
- 如何在Heroku上重新启动rails服务器?
- respond_to块如何工作?
- 我可以把我所有的http://链接都改成//吗?
- 给定一个类,查看实例是否有方法(Ruby)
- rails局部模板中的可选局部变量:如何退出(defined?foo)混乱?
- URL为AJAX请求编码一个jQuery字符串
- 在日历应用程序中建模重复事件的最佳方法是什么?
- Ruby Bundle Symbol not found: _SSLv2_client_method (LoadError)
- 编译System.Net.HttpClient的查询字符串
- 摘要认证和基本认证的区别是什么?
- Axios -删除请求与请求体和头?