您将 Rack 中间件文件和要求放在哪里?
Posted
技术标签:
【中文标题】您将 Rack 中间件文件和要求放在哪里?【英文标题】:Where do you put your Rack middleware files and requires? 【发布时间】:2011-03-26 14:15:57 【问题描述】:我正在将 Rails 应用程序中内置的一些逻辑重构为中间件,我遇到的一个烦恼是似乎缺乏放置它们的约定。
目前我已选择app/middleware
,但我可以轻松地将其移至vendor/middleware
或vendor/plugins/middleware
...
最大的问题是必须要求config/environment.rb
顶部的单个文件
require "app/middleware/system_message"
require "app/middleware/rack_backstage"
否则我会在 config.middleware.use
行上收到未初始化的常量错误。这很快就会变得一团糟。我宁愿把它藏在某个初始化器中。
有没有常规的地方放这些东西?
我正在寻找这个赏金的具体答案是:我可以在哪里放置 require 行,这样它们就不会弄乱 environment.rb 文件,但仍然会在 config.middleware.use 调用之前加载? 我尝试过的一切都会导致未初始化的常量错误。
更新:现在我们使用的是 Rails 3.0,我将 Rails 应用程序视为任何其他 Rack 应用程序;中间件的代码文件放在lib
(或Gemfile
中列出的gem)中,需要并在config.ru
中加载。
【问题讨论】:
【参考方案1】:从 Rails 3.2 开始,Rack 中间件属于 app/middleware 目录。
它“开箱即用”,无需任何明确的 require 语句。
快速示例:
我正在使用一个名为 CanonicalHost 的中间件类,它在 app/middleware/canonical_host.rb 中实现。我在 production.rb 中添加了以下行(注意中间件类是明确给出的,而不是作为引用字符串,它适用于任何特定于环境的配置文件):
config.middleware.use CanonicalHost, "example.com"
如果您要向 application.rb 添加中间件,则需要根据@mltsy's comment 包含引号。
config.middleware.use "CanonicalHost", "example.com"
【讨论】:
能否添加任何链接以供参考?它对我来说不是开箱即用的,而且 Google / Rails 指南也没有帮助......谢谢! 在我意识到我犯了一个愚蠢的错误,将它放在我的根文件夹而不是(已经指定的)应用程序文件夹中之后,我得到了这个开箱即用的工作。所以,foo_app/app/middleware/file.rb. 我必须用引号将我的中间件类名括起来才能让它工作;请参阅下面的答案。 注意:只有在特定环境文件(即production.rb
)中添加中间件时,这才有效(不带引号) - 如果您想通过将其添加到所有环境中将其添加到 @ 987654326@,您必须使用引号(或明确要求定义您的类的文件),因为 application.rb 是首先加载的内容之一 - 在任何动态初始化代码加载 app/middleware
文件之前。跨度>
注意,在 Rails 5+ 中,您不能再使用字符串作为中间件的名称。您需要使用正确的类名。此外,根据@rafaelfranca(Rails 核心)的说法,“中间件不能在应用程序中,因为它们无法重新加载。它们应该在 lib 中,如果你将它们放在 lib 中,require_relative 将起作用。” i> github.com/rails/rails/issues/25525#issuecomment-479941866【参考方案2】:
你可以把它放在lib/tableized/file_name.rb
。只要您尝试加载的类可以通过其文件名发现,Rails 就会自动加载必要的文件。所以,例如:
config.middleware.use "MyApp::TotallyAwesomeMiddleware"
你会留在:
lib/my_app/totally_awesome_middleware.rb
Rails 捕获 const_missing 并尝试自动加载与丢失的常量对应的文件。只要确保你的名字匹配并且你是肉汁。 Rails 甚至提供了漂亮的助手,可以帮助您轻松识别文件的路径:
>> ChrisHeald::StdLib.to_s.tableize.singularize
=> "chris_heald/std_lib"
所以我的标准库位于lib/chris_heald/std_lib.rb
,当我在代码中引用它时会自动加载。
【讨论】:
我有一些名为Rack::Backstage
的中间件。把它放在lib/rack/backstage.rb
。初始化常量错误。
这仅在第一个参数是字符串时有效。一旦我想通了,成功!【参考方案3】:
在我的 Rails 3.2 应用程序中,正如@MikeJarema 所述,我可以通过将中间件TrafficCop
放在app/middleware/traffic_cop.rb
来加载它。然后我按照指示将此行添加到我的config/application.rb
:
config.middleware.use TrafficCop
但是,在应用程序启动时,我不断收到此错误:
uninitialized constant MyApp::Application::TrafficCop
明确指定根命名空间也无济于事:
config.middleware.use ::TrafficCop
# uninitialized constant TrafficCop
由于某种原因(我还没有发现),在 Rails 生命周期的这一点上,app/middleware
没有包含在加载路径中。如果我删除了config.middleware.use
行并运行控制台,我可以毫无问题地访问TrafficCop
常量。但在配置时在app/middleware
中找不到它。
我通过将中间件类名称括在引号中来解决此问题,如下所示:
config.middleware.use "TrafficCop"
这样,我可以避免 uninitialized constant
错误,因为 Rails 还没有尝试找到 TrafficCop
类。但是,当它开始构建中间件堆栈时,它会将字符串常量化。此时,app/middleware
已在加载路径中,因此该类将正确加载。
【讨论】:
我在 Rails 3.2.18 上,当我把它放在 application.rb 中时遇到同样的问题 - 但是当我把它放在特定的环境配置中时,它工作得很好!如果您遵循以 config.ru 开头的加载路径,结果是 application.rb 在大多数其他内容之前加载,然后应用程序被初始化(它加载所有动态初始化程序,包括中间件),其他环境文件在之后加载那个,所以到那时中间件常量就定义好了! 注意,在 Rails 5+ 中,您不能再使用字符串作为中间件的名称。您需要使用正确的类名。此外,根据@rafaelfranca(Rails 核心)的说法,“中间件不能在应用程序中,因为它们无法重新加载。它们应该在 lib 中,如果你将它们放在 lib 中,require_relative 将起作用。” i> github.com/rails/rails/issues/25525#issuecomment-479941866【参考方案4】:对于 Rails 3:
#config/application.rb
require 'lib/rack/my_adapter.rb'
module MyApp
class Application < Rails::Application
config.middleware.use Rack::MyAdapter
end
end
【讨论】:
这仍然有效 - 并且比所有其他字符串答案更好 - 在 Rails 5+ 中,他们不允许使用字符串作为中间件的名称。【参考方案5】:我不知道约定,但为什么不把它放在/lib
目录中呢? Rails 会自动加载其中的文件。
【讨论】:
直到 environment.rb 运行之后,如果有的话。将它们移动到 lib 并不能解决未初始化的常量错误。【参考方案6】:您可以创建一个需要必要文件的初始化程序,然后将文件放在您想要的任何地方。
根据this,初始化程序在机架中间件加载之前执行。
【讨论】:
看起来我在阅读时有点乐观。我猜 config.ru 也不符合你的要求? 嗯,这需要使用 rackup 而不是脚本/服务器,对吧?如果必须,我可以这样做,但老实说,我认为这会比这更容易。【参考方案7】:到目前为止,我的工作解决方案是将中间件要求移至 config/middleware.rb
并要求 environment.rb
中的文件,将其减少为我可以接受的单个要求。
我仍然想听听其他人是如何解决向 Rails 添加中间件这个看似基本的问题的。
【讨论】:
以上是关于您将 Rack 中间件文件和要求放在哪里?的主要内容,如果未能解决你的问题,请参考以下文章
iOS 8 JavaScriptCore:您将 JavaScript 文件放在哪里?
在 Ruby on Rails 的上下文中引用时,啥是中间件?