注意这最初是关于404错误的问题,但现在问题是我应用的补丁会产生什么影响。
如何获得缓存操作以在所有引发ActiveRecord :: RecordNotFound异常的请求上返回404,而不仅仅是第一个请求?
例如,如果你启动一个空的rails项目,添加一个产品模型和控制器,设置你的database.yml,在production.rb中设置你的缓存后端,rake db:migrate,然后开始生产并点击网站一个不存在的对象,例如http://localhost:3000/product/show/1234
class ProductController < ApplicationController
caches_action :show
def show
@product = Product.find(params[:id])
render :text => "asdf"
end
end
第一次点击页面时,它会按预期返回404页面。但是,对该URL的每次后续匹配都会返回一个200 OK的空白页面。你如何让它每次都返回404?
以下是CURL请求,后跟日志
~ $ curl -I http://0.0.0.0:3000/product/show/1234
HTTP/1.1 404 Not Found
Connection: close
Date: Mon, 20 Apr 2009 22:49:18 GMT
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
Content-Length: 14097
~ $ curl -I http://0.0.0.0:3000/product/show/1234
HTTP/1.1 200 OK
Connection: close
Date: Mon, 20 Apr 2009 22:49:19 GMT
X-Runtime: 6
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
Content-Length: 0
第二种反应显然是错误的。
以下是2个请求的日志副本:
Processing ProductController#show (for 127.0.0.1 at 2009-04-20 17:35:24) [GET]
Parameters: {"id"=>"1234"}
ActiveRecord::RecordNotFound (Couldn't find Product with ID=1234):
app/controllers/product_controller.rb:6:in `show'
Rendering rescues/layout (not_found)
Processing ProductController#show (for 127.0.0.1 at 2009-04-20 17:35:30) [GET]
Parameters: {"id"=>"1234"}
Filter chain halted as [#<ActionController::Caching::Actions::ActionCacheFilter:0x23e36d4 @options={:cache_path=>nil, :store_options=>{}, :layout=>nil}>] rendered_or_redirected.
Filter chain halted as [#<ActionController::Filters::AroundFilter:0x23e3580 @kind=:filter, @options={:unless=>nil, :if=>nil, :only=>#<Set: {"show"}>}, @method=#<ActionController::Caching::Actions::ActionCacheFilter:0x23e36d4 @options={:cache_path=>nil, :store_options=>{}, :layout=>nil}>, @identifier=nil>] did_not_yield.
Completed in 12ms (View: 0, DB: 0) | 200 OK [http://0.0.0.0/product/show/1234]
实际上,如果你将缓存的操作从缓存中拉出来,那里就会有一些空的垃圾。
cache.fetch("views/0.0.0.0:3000/product/show/1234")
=> ["", nil, [], []]
我在这里做错了什么?
修改
我已经确认Rails 2.1.2和2.2.2没有表现出这种行为,但2.3.2确实如此。 (即旧版本不会将空响应存储到缓存中,并且确实会为后续请求抛出404)
我在测试边缘Rails时遇到问题,因为在启动服务器时加载它会导致以下错误: foobar / vendor / rails / activesupport / lib / active_support / dependencies.rb:440:in`load_missing_constant':uninitialized constant ActionController :: Failsafe(NameError)
我已经对2-3稳定分支375e8976e3的当前负责人进行了测试,它也表现出这种行为。
编辑#2 我试图跟踪Rails代码库中发生更改的时间,以确定它是否是故意的。似乎this seemingly innocuous commit是bug开始的地方。
以下是二分的细节,其中404表示期望的行为,200表示不期望的行为。
2-3-stable branch 375e8976e3 - 200 b1c989f28d - 200 beca1f2e15 - 200 f1fff0a48 - 200 f1e20ce9a7 - 200 a5004573d8 - 200 2e1132fad8 - 200 - the difference seems to start at this commit c69d8c043f - 404 d961592886 - 404 276ec16007 - 404 0efec6452 - 404 13c6c3cfc5 - 404 fb2325e35 - 404 2-2 stable 3cb89257b4 - 404
这是一个反转更改的补丁,当应用于标记v2.3.2.1,即dc88847e5ce392eed210b97525c14fca55852867时,修复了问题。然而,我不够聪明,不知道为什么这个看似微不足道的变化实际上会有所作为! 也许比我聪明的人可以对这种情况有所了解?
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 0facf70..0790807 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -1403,12 +1403,9 @@ module ActionController #:nodoc:
end
Base.class_eval do
- [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
- Cookies, Caching, Verification, Streaming, SessionManagement,
- HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods,
- RecordIdentifier, RequestForgeryProtection, Translation
- ].each do |mod|
- include mod
- end
+ include Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers
+ include Cookies, Caching, Verification, Streaming, SessionManagement
+ include HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods
+ include RecordIdentifier, RequestForgeryProtection, Translation
end
end
编辑#3 补丁似乎也修复了上面显示的相关错误,其中“已完成的XYms(DB:Z)| 404未找到[http://0.0.0.0/product/1234]”未显示在日志中。
编辑#4 上面的补丁破坏了ActionPack中的其他内容,因此我深入研究并生成了一个不会导致附带损害的问题。补丁和任何后续更新都将在the rails lighthouse
答案 0 :(得分:16)
include(X, Y, Z)
实际上的运作顺序与include X; include Y; include Z
不同。
下面我粘贴了在Ruby 1.8.6中实现Module#include方法的C代码。
static VALUE
rb_mod_include(argc, argv, module)
int argc;
VALUE *argv;
VALUE module;
{
int i;
for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE);
while (argc--) {
rb_funcall(argv[argc], rb_intern("append_features"), 1, module);
rb_funcall(argv[argc], rb_intern("included"), 1, module);
}
return module;
}
即使你不熟悉Ruby的C内部,很明显这个函数有一个for循环向上迭代来检查所有参数的类型是否为T_MODULE,然后使用while循环迭代向下实际包含模块 - 因此include(X, Y, Z)
中的模块实际上将包含在Z, Y, X
的顺序中。我还没有浏览过所有相关的Rails模块,但我想有一些依赖于顺序的东西,一旦切换包含顺序就会失败。