Rails中default_url_options与路径/url辅助方法参数冲突如何解决?
问题表现
在Rails 6.1.4应用中引入routing-filter Gem的locale过滤器实现URL带语言前缀(如/en/articles)后,路径辅助方法传入params或其他可选参数时会触发路由匹配错误,无额外参数时调用完全正常。
复现代码
# test/controllers/articles_controller_test.rb patch article_url(@article, params: { article: my_hash })
错误信息
# Error output ArticlesControllerTest#test_should_update: DRb::DRbRemoteError: No route matches {:action=>"show", :controller=>"articles", :locale=>#<Article id: 805413029, ...>}, missing required keys: [:id] (ActionController::UrlGenerationError) test/controllers/articles_controller_test.rb:58:in `block in <class:ArticlesControllerTest>'
错误原因是路径辅助方法将传入的模型实例@article错误识别为locale参数值,导致路由缺少必填的id参数。
原有配置
# test_helper.rb (routing-filter参考文档推荐配置) RoutingFilter.active = false # application_controller.rb def default_url_options Rails.application.default_url_options = Rails.application.routes.default_url_options = { locale: I18n.locale } end # config/routes.rb Rails.application.routes.draw do filter :locale scope "(:locale)", locale: /en|ja|fr/ do # 该scope可删除,多余配置有时会导致路径中的locale重复,引发问题 resources :articles end end
注意:当前使用的是routing-filter主分支最新提交(8d1f1da),RubyGems发布的0.6.3版本已知不兼容Rails 6.1。
解决方案
第一步:移除路由冗余配置
首先删除routes.rb中包裹资源的(:locale) scope,routing-filter的locale过滤器会自动处理路径前缀,不需要额外手动声明scope,重复配置会干扰参数解析逻辑。
第二步:修正default_url_options赋值逻辑
ApplicationController中的default_url_options不需要每次调用都覆盖全局配置,调整为如下写法即可,保证默认参数可以被路径辅助方法正常识别:
# application_controller.rb def default_url_options { locale: I18n.locale } end
第三步:测试环境配置调整
测试环境中默认不会自动加载控制器的default_url_options,在test_helper.rb中添加如下配置,让测试环境的路径辅助方法也能自动带上locale参数:
# test_helper.rb class ActionDispatch::IntegrationTest def default_url_options { locale: I18n.default_locale } # 可根据测试需求指定对应locale end end
调整完成后所有路径辅助方法都可以正常传参,不需要手动合并default_url_options,原有的调用代码patch article_url(@article, params: { article: my_hash })可以直接正常运行。
临时替代方案(如暂时无法修改全局配置)
如果因为项目限制无法调整全局配置,可以在路径辅助方法中先显式传入locale参数,避免模型被识别为locale值:
article_url(locale: I18n.locale, id: @article, params: { article: my_hash })
内容的提问来源于stack exchange,提问作者Masa Sakano

