Ruby中的机架中间件是什么?对于他们所说的“中间件”,我找不到任何好的解释。


当前回答

机架作为设计

机架中间件不仅仅是“一种过滤请求和响应的方式”——它是使用机架的web服务器管道设计模式的实现。

它非常清晰地分离了处理请求的不同阶段——关注点分离是所有设计良好的软件产品的关键目标。

例如,使用Rack,我可以有管道的单独阶段:

Authentication: when the request arrives, are the users logon details correct? How do I validate this OAuth, HTTP Basic Authentication, name/password? Authorization: "is the user authorised to perform this particular task?", i.e. role-based security. Caching: have I processed this request already, can I return a cached result? Decoration: how can I enhance the request to make downstream processing better? Performance & Usage Monitoring: what stats can I get from the request and response? Execution: actually handle the request and provide a response.

能够分离不同的阶段(并可选地包括它们)对于开发结构良好的应用程序非常有帮助。

社区

还有一个围绕机架中间件开发的很好的生态系统——你应该能够找到预先构建的机架组件来完成上面所有的步骤。请参阅机架GitHub维基获取中间件列表。

中间件是什么?

中间件是一个可怕的术语,它指的是任何帮助但不直接参与某些任务执行的软件组件/库。非常常见的例子是日志记录、身份验证和其他常见的水平处理组件。这些往往是跨多个应用程序的每个人都需要的东西,但没有太多人对自己构建感兴趣(或应该有兴趣)。

更多的信息

关于它是过滤请求的一种方式的评论可能来自RailsCast第151集:机架中间件的屏幕放映。 机架中间件从机架发展而来,在机架中间件介绍中有一个很好的介绍。 维基百科上有关于中间件的介绍。

其他回答

机架作为设计

机架中间件不仅仅是“一种过滤请求和响应的方式”——它是使用机架的web服务器管道设计模式的实现。

它非常清晰地分离了处理请求的不同阶段——关注点分离是所有设计良好的软件产品的关键目标。

例如,使用Rack,我可以有管道的单独阶段:

Authentication: when the request arrives, are the users logon details correct? How do I validate this OAuth, HTTP Basic Authentication, name/password? Authorization: "is the user authorised to perform this particular task?", i.e. role-based security. Caching: have I processed this request already, can I return a cached result? Decoration: how can I enhance the request to make downstream processing better? Performance & Usage Monitoring: what stats can I get from the request and response? Execution: actually handle the request and provide a response.

能够分离不同的阶段(并可选地包括它们)对于开发结构良好的应用程序非常有帮助。

社区

还有一个围绕机架中间件开发的很好的生态系统——你应该能够找到预先构建的机架组件来完成上面所有的步骤。请参阅机架GitHub维基获取中间件列表。

中间件是什么?

中间件是一个可怕的术语,它指的是任何帮助但不直接参与某些任务执行的软件组件/库。非常常见的例子是日志记录、身份验证和其他常见的水平处理组件。这些往往是跨多个应用程序的每个人都需要的东西,但没有太多人对自己构建感兴趣(或应该有兴趣)。

更多的信息

关于它是过滤请求的一种方式的评论可能来自RailsCast第151集:机架中间件的屏幕放映。 机架中间件从机架发展而来,在机架中间件介绍中有一个很好的介绍。 维基百科上有关于中间件的介绍。

我使用Rack中间件解决了几个问题:

使用自定义机架中间件捕获JSON解析错误,并在客户端提交错误JSON时返回格式化良好的错误消息 内容压缩通过机架::Deflater

在这两种情况下,它都提供了相当优雅的修复。

在很长一段时间里,我自己都无法理解Rack。我是在自己制作了这个小型Ruby web服务器之后才完全理解它的。我在我的博客http://blog.gauravchande.com/what-is-rack-in-ruby-rails上分享了我对Rack的了解(以故事的形式)

非常欢迎反馈。

机架是什么?

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回购提交) 好的博文,“机架中间件介绍” 一些好的机架文档

Rack提供了一个简单的接口来抽象HTTP请求/响应。Rack位于web框架(Rails, Sinatra等)和web服务器(unicorn, puma)之间,作为适配器。从上图来看,这使得unicorn服务器完全独立于了解rails的服务器,而rails不知道unicorn的服务器。这是一个松耦合、关注点分离的好例子。

上图来自机架https://youtu.be/3PnUV9QzB0g上的rails会议演讲,我建议观看它以获得更深入的理解。