Ruby on rails 什么是机架中间件?

Ruby on rails 什么是机架中间件?,ruby-on-rails,ruby,http,web-applications,rack,Ruby On Rails,Ruby,Http,Web Applications,Rack,Ruby中的机架中间件是什么?对于“中间件”的含义,我找不到任何好的解释。Rack as Design 机架中间件不仅仅是“一种过滤请求和响应的方法”——它是一种用于web服务器的实现 它非常清晰地划分了处理请求的不同阶段——分离关注点是所有精心设计的软件产品的关键目标 例如,对于Rack I,管道的各个阶段可以执行以下操作: 身份验证:当请求到达时,用户登录详细信息是否正确?如何验证此OAuth、HTTP基本身份验证、名称/密码 授权:“是否授权用户执行此特定任务?”,即基于角色的安全性 缓

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

Rack as Design 机架中间件不仅仅是“一种过滤请求和响应的方法”——它是一种用于web服务器的实现

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

例如,对于Rack I,管道的各个阶段可以执行以下操作:

  • 身份验证:当请求到达时,用户登录详细信息是否正确?如何验证此OAuth、HTTP基本身份验证、名称/密码

  • 授权:“是否授权用户执行此特定任务?”,即基于角色的安全性

  • 缓存:我是否已处理此请求,是否可以返回缓存结果

  • 装饰:如何提高要求,使下游处理更好

  • 性能和使用监控:我可以从请求和响应中获得哪些统计数据

  • 执行:实际处理请求并提供响应

在开发结构良好的应用程序时,能够分离不同的阶段(并且可以选择包括它们)是一个很大的帮助

社区 还有一个围绕机架中间件开发的伟大生态系统-您应该能够找到预构建的机架组件来完成上述所有步骤和更多步骤。看

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

更多信息
  • 关于它是过滤请求的一种方式的评论可能来自屏幕上的演员

  • 机架中间件从机架发展而来,在上有一个很好的介绍

  • 维基百科上有一个关于中间件的介绍


首先,机架就是两件事:

  • Web服务器接口约定
  • 宝石
机架-Web服务器界面

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)
rack的基础是一个简单的约定。每个机架兼容的Web服务器都会对您提供给他的对象调用一个call方法,并提供该方法的结果。Rack精确地指定此调用方法的外观,以及它必须返回的内容。那太棒了

让我们简单地试一下。我将使用WEBrick作为机架兼容的Web服务器,但任何一个都可以。让我们创建一个返回JSON字符串的简单web应用程序。为此,我们将创建一个名为config.ru的文件。机架gem的命令rackup将自动调用config.ru,该命令将在机架兼容的Web服务器中运行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
按照约定,我们的服务器有一个名为call的方法,该方法接受环境散列,并返回一个格式为[status,headers,body]的数组,供Web服务器使用。让我们通过简单地调用rackup来尝试一下。默认的机架兼容服务器(可能是WEBrick或Mongrel)将启动并立即等待服务请求

$ 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
让我们通过卷曲或访问url
http://localhost:9292/hello.json
瞧:

$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
它起作用了。伟大的这是每个web框架的基础,无论是Rails还是Sinatra。在某种程度上,他们实现了一个调用方法,完成了所有的框架代码,最后以典型的[status,Header,body]形式返回响应

例如,在RubyonRails中,rack请求点击
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
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   
因此,基本上是Rails检查,如果有任何路由匹配,则依赖于env散列。如果是这样,它会将env散列传递给应用程序以计算响应,否则它会立即使用404进行响应。因此,任何符合机架接口约定的Web服务器都能够为成熟的Rails应用程序提供服务

中间件

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)
Rack还支持创建中间件层。他们基本上是截取一个请求,对它做一些事情,然后传递它。这对于多功能任务非常有用

假设我们希望向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的一个实例。Rack会自动调用中间件上的call方法,并期望返回一个
[状态、标题、正文]
数组,就像我们的JSONServer返回的一样

因此,在这个中间件中,取起点,然后使用
@app.call(env)
对JSONServer进行实际调用,然后记录器输出日志记录条目,最后返回响应
[@status,@headers,@body]

要使我们的小rackup.ru使用此中间件,请向其添加一个use RackLogger,如下所示:

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
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   
重新启动服务器,瞧,它会在每个请求上输出一个日志。Rack允许您添加多个按添加顺序调用的中间件。这是一种在不改变机架应用程序核心的情况下添加功能的好方法

机架-宝石

虽然机架首先是一种惯例,但它也是一种提供强大功能的宝石。其中一个我们已经用于JSON服务器,rackup命令。但还有更多!li说,rack gem为许多用例提供了很少的应用程序
main
Middleware
#./message_app.rb
class MessageApp
  def call(env)
    [200, {}, ['Hello, World!']]
  end
end

#./config.ru
require_relative './message_app'

run MessageApp.new
#./message_app.rb
class MessageApp
  def call(env)
    message = env['QUERY_STRING']
    [200, {}, [message]]
  end
end
#./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
require_relative './message_app'
require_relative './middleware/message_setter'

use MessageSetter
run MessageApp.new
class MessageApp
  def call(env)
    Rack::Response.new([env['MESSAGE']], 200, {})
  end
end
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
rackup -p PORT_NUMBER