Rails Minitest中添加参数后GET请求变POST的原因及解决方法
问题
我在Rails应用里编写分页的index动作,控制器代码如下:
def index @pokemons = Pokemon.all.limit(params[:limit]).offset(params[:offset]) end
这段代码在开发服务器通过浏览器请求时完全正常,但在Minitest测试中调用index动作时遇到问题:
- 不添加参数时,执行如下测试代码会发送GET请求并路由到
#index:
test "should return paginated index" do get pokemons_url, as: :json assert_response :success end
- 但添加分页参数后,服务器收到的请求变为POST并路由到
#create动作:
test "should return paginated index" do get pokemons_url, params: {limit: 10, offset: 0}, as: :json assert_response :success end
我的config/routes.rb代码如下:
Rails.application.routes.draw do resources :pokemons end
rails routes的相关输出为:
pokemons GET /pokemons(.:format) pokemons#index POST /pokemons(.:format) pokemons#create pokemon GET /pokemons/:id(.:format) pokemons#show PATCH /pokemons/:id(.:format) pokemons#update PUT /pokemons/:id(.:format) pokemons#update DELETE /pokemons/:id(.:format) pokemons#destroy
请问为何在Minitest的get方法中添加参数会导致请求方法从GET变为POST?如何在Minitest中发送包含分页参数的GET请求?
解答
原因
问题出在Rails测试中get方法的参数解析逻辑上。在Rails 5及更早版本中,测试里的get方法采用位置参数格式:get(path, params, headers);而你使用了Rails 6+引入的关键字参数写法(params: ..., as: ...),这种情况下Rails会错误地将params哈希识别为请求体参数。由于GET请求规范中通常不包含请求体,Rails会自动将该请求转换为POST请求(这是框架的兼容处理逻辑),最终匹配到#create路由。
解决方法
有两种规范的写法可以正确发送带分页参数的GET请求:
- 位置参数写法(兼容全版本)
将分页参数作为第二个位置参数传递,格式参数放在第三个位置的headers哈希中:
test "should return paginated index" do get pokemons_url, { limit: 10, offset: 0 }, { format: :json } assert_response :success end
- 关键字参数写法(Rails 6+推荐)
直接将分页参数传入pokemons_url生成带查询参数的URL,这样Rails会明确将其处理为GET请求的查询参数,不会触发请求方法转换:
test "should return paginated index" do get pokemons_url(limit: 10, offset: 0), as: :json assert_response :success end
也可以通过显式指定method: :get来强制请求方法:
test "should return paginated index" do get pokemons_url, params: { limit: 10, offset: 0 }, as: :json, method: :get end
内容的提问来源于stack exchange,提问作者dB'
相关产品推荐
相关产品推荐

