@ app.call(env)真正做了什么?

时间:2017-09-26 21:49:38

标签: ruby express middleware rack

我真的很想知道这些东西是如何运作的,特别是在技术方面。目前,我正在更深入地研究ruby并尝试仅将其用于机架,以便了解基于机架的框架的工作原理。

此时,机架中间件让我发疯。为什么?虽然中间件非常简单,但我对@app.call(env)有点困惑。为清楚起见,请考虑以下代码:

class MyCustomMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    @app.call(env) if env['REQUEST_METHOD'] != 'POST'

    body = env['rack.input'].clone
    body = JSON.parse(body.gets || {}, symbolize_names: true)
    body[:some_message] = "Peace, Love and Hope"

    env.update('rack.input', StringIO.new(JSON.dump(body)))

    @app.call(env)
  env
end

如果(且仅当)请求方法为POST,我想要做的就是更改请求正文。如果请求方法是除“POST”之外的任何其他类型,我想将请求传递给下一个中间件(它在Rack中以这种方式工作,对吧?)。问题是,无论请求方法是否为POST,所有代码都在执行。

对于机架中间件而言,这可能是一个误解,因为我习惯使用Express.js。在Express中,您有一堆请求通过的中间件,并且每个中间件都调用next()方法以“释放”请求。我认为@app.call(env)Express'next()方法类似......但看起来并非如此,因为当我调用它并且所有代码都是被执行。

有人可以解释一下这个方法到底做了什么,并指出我的错误在哪里?

1 个答案:

答案 0 :(得分:8)

@app.call不会终止处理程序的执行 - 它只是调用链中的下一个中间件。预计每个中间件将调用链中的下一个并返回其返回值,或者通过返回[status_code, body, headers]数组来终止链。期望每个中间件通过从[status_code, body, headers]方法返回该值来将#call数组传递回链。回想一下,在Ruby中,每个方法的最后一个语句的返回值都隐式返回给它的调用者。

如上所述,您将调用堆栈中剩余的中间件,然后丢弃其结果,然后继续使用您的处理程序,运行代码,再次调用剩余的中间件堆栈 ,然后最终将结果返回上游。

如果您想摆脱处理程序,请明确return

def call(env)
  return @app.call(env) if env['REQUEST_METHOD'] != 'POST'

  body = env['rack.input'].clone
  body = JSON.parse(body.gets || {}, symbolize_names: true)
  body[:some_message] = "Peace, Love and Hope"

  env.update('rack.input', StringIO.new(JSON.dump(body)))

  @app.call(env)
end

可能更清楚的是有条件地运行mutators,然后总是@app.call来终止处理程序:

def call(env)
  mutate!(env) if env['REQUEST_METHOD'] == "POST"
  @app.call(env)
end

def mutate!(env)
  body = env['rack.input'].clone
  body = JSON.parse(body.gets || {}, symbolize_names: true)
  body[:some_message] = "Peace, Love and Hope"
  env.update('rack.input', StringIO.new(JSON.dump(body)))
end

由于@app.call#call中的最后一个语句,因此将其返回值返回给中间件的调用者。