我编写了自己的中间件来为我们的应用程序提供API端点。中间件加载提供API方法的类,并将请求路由到适当的类/方法。这些类通过String#constantize
动态加载。
在开发模式下运行时,会自动重新加载类。但是,如果存在未捕获的异常 - 随后由故障安全中间件处理 - 自动重新加载将停止工作。 constantize
仍然被调用,但它似乎返回了旧班。
看起来还有其他东西可以卸载类,而未捕获的异常会破坏它。这可能是什么?
运行Ruby 1.8.7,Rails 2.3.3和Thin 1.2.2。
答案 0 :(得分:1)
我认为这种效果来自ActionController::Reloader
的编写方式。这是2.3.3中的ActionController::Reloader#call
,请注意评论:
def call(env)
Dispatcher.reload_application
status, headers, body = @app.call(env)
# We do not want to call 'cleanup_application' in an ensure block
# because the returned Rack response body may lazily generate its data. This
# is for example the case if one calls
#
# render :text => lambda { ... code here which refers to application models ... }
#
# in an ActionController.
#
# Instead, we will want to cleanup the application code after the request is
# completely finished. So we wrap the body in a BodyWrapper class so that
# when the Rack handler calls #close during the end of the request, we get to
# run our cleanup code.
[status, headers, BodyWrapper.new(body)]
end
Dispatcher.reload_application
不会删除自动加载的常量Dispatcher.cleanup_application
。编写BodyWrapper#close
时可能会出现例外情况:
def close
@body.close if @body.respond_to?(:close)
ensure
Dispatcher.cleanup_application
end
但是这没有用,因为如果@app.call
中的ActionController::Reloader#call
引发异常,BodyWrapper
不会被实例化,Dispatcher.cleanup_application
也不会被调用。
想象一下以下场景:
传统控制器出现错误时不会发生这种情况,因为这些错误由ActionController::Rescue
处理。此类异常未命中ActionController::Reloader
。
最简单的解决方案是将fallback rescue子句放入API路由中间件,其中有一些变体:
def call(env)
# route API call
resuce Exception
Dispatcher.cleanup_application
raise
end
请注意,这是我对3岁问题的回答,我跟随了2.3.3的调用堆栈。较新版本的rails可能会以不同的方式处理事情。
答案 1 :(得分:0)
Rails缓存了许多类,并在开发模式下或在config.cache_classes设置为true时卸载并重新加载它们。以下是关于该主题的一些想法,也解释了它的工作原理。 http://www.spacevatican.org/2008/9/28/required-or-not/
不要告诉你,你做错了,但重载String#constantize似乎是一种重新加载你的代码的hacky方式。您是否考虑使用watchr之类的东西在开发中运行您的应用服务器,并在您的API子树中保存文件时重新启动它? https://github.com/mynyml/watchr/
此外,有关如何进一步调试的随机提示,请查看以下答案:https://stackoverflow.com/a/7907289/632022