Rails 强参数 - 无需密钥即可允许请求
Rails strong parameters - Request allowed without required key
我正在开发 Rails API 并且我在控制器中使用了强大的参数。我有一个请求规范,该规范对一个模型失败但适用于所有其他模型。每个型号的控制器几乎都是一样的。
正如您在下面的规范中看到的,请求主体应该是 { "tag": { "name": "a good name" }}
。但是,此规范使用的 { "name": "a good name" }
应该无效,因为它缺少 "tag" 键。相同控制器功能的相同规格适用于许多其他型号。
另一个有趣的转折是,如果我将控制器的强参数更改为 params.require(:not_tag).permit(:name)
,它会抛出一个错误,因为不包括 "not_tag" 键。
- Ruby: 2.6.5p114
- Rails: 6.0.1
- 预期响应状态:422
- 收到回复状态:201
控制器
class TagsController < ApplicationController
before_action :set_tag, only: [:show, :update, :destroy]
# Other methods...
# POST /tags
def create
@tag = Tag.new(tag_params)
if @tag.save
render "tags/show", status: :created
else
render json: @tag.errors, status: :unprocessable_entity
end
end
# Other methods...
private
# Use callbacks to share common setup or constraints between actions.
def set_tag
@tag = Tag.find_by(id: params[:id])
if !@tag
object_not_found
end
end
# Only allow a trusted parameter "white list" through.
def tag_params
params.require(:tag).permit(:name)
end
# render response for objects that aren't found
def object_not_found
render :json => {:error => "404 not found"}.to_json, status: :not_found
end
end
请求规范
require 'rails_helper'
include AuthHelper
include Requests::JsonHelpers
RSpec.describe "Tags", type: :request do
before(:context) do
@user = create(:admin)
@headers = AuthHelper.authenticated_header(@user)
end
# A bunch of other specs...
describe "POST /api/tags" do
context "while authenticated" do
it "fails to create a tag from malformed body with 422 status" do
malformed_body = { "name": "malformed" }.to_json
post "/api/tags", params: malformed_body, headers: @headers
expect(response).to have_http_status(422)
expect(Tag.all.length).to eq 0
end
end
end
# A bunch of other specs...
after(:context) do
@user.destroy
@headers = nil
end
end
此行为是因为 ParamsWrapper 功能在 Rails 6 中默认启用。wrap_parameters
将接收到的参数包装到嵌套哈希中。因此,这允许客户端发送请求而无需在根元素中嵌套数据。
例如,在一个名为Tag
的模型中,它基本上转换
{
name: "Some name",
age: "Some age"
}
到
{
tag:
{
name: "Some name",
age: "Some age"
}
}
但是,正如您在测试中看到的那样,如果您将所需的键更改为 not_tag
,则包装会按预期中断 API 调用。
可以使用 config/initializers/wrap_parameters.rb
文件更改此配置。在该文件中,您可以将 wrap_parameters format: [:json]
设置为 wrap_parameters format: []
以禁止此类参数包装。
我正在开发 Rails API 并且我在控制器中使用了强大的参数。我有一个请求规范,该规范对一个模型失败但适用于所有其他模型。每个型号的控制器几乎都是一样的。
正如您在下面的规范中看到的,请求主体应该是 { "tag": { "name": "a good name" }}
。但是,此规范使用的 { "name": "a good name" }
应该无效,因为它缺少 "tag" 键。相同控制器功能的相同规格适用于许多其他型号。
另一个有趣的转折是,如果我将控制器的强参数更改为 params.require(:not_tag).permit(:name)
,它会抛出一个错误,因为不包括 "not_tag" 键。
- Ruby: 2.6.5p114
- Rails: 6.0.1
- 预期响应状态:422
- 收到回复状态:201
控制器
class TagsController < ApplicationController
before_action :set_tag, only: [:show, :update, :destroy]
# Other methods...
# POST /tags
def create
@tag = Tag.new(tag_params)
if @tag.save
render "tags/show", status: :created
else
render json: @tag.errors, status: :unprocessable_entity
end
end
# Other methods...
private
# Use callbacks to share common setup or constraints between actions.
def set_tag
@tag = Tag.find_by(id: params[:id])
if !@tag
object_not_found
end
end
# Only allow a trusted parameter "white list" through.
def tag_params
params.require(:tag).permit(:name)
end
# render response for objects that aren't found
def object_not_found
render :json => {:error => "404 not found"}.to_json, status: :not_found
end
end
请求规范
require 'rails_helper'
include AuthHelper
include Requests::JsonHelpers
RSpec.describe "Tags", type: :request do
before(:context) do
@user = create(:admin)
@headers = AuthHelper.authenticated_header(@user)
end
# A bunch of other specs...
describe "POST /api/tags" do
context "while authenticated" do
it "fails to create a tag from malformed body with 422 status" do
malformed_body = { "name": "malformed" }.to_json
post "/api/tags", params: malformed_body, headers: @headers
expect(response).to have_http_status(422)
expect(Tag.all.length).to eq 0
end
end
end
# A bunch of other specs...
after(:context) do
@user.destroy
@headers = nil
end
end
此行为是因为 ParamsWrapper 功能在 Rails 6 中默认启用。wrap_parameters
将接收到的参数包装到嵌套哈希中。因此,这允许客户端发送请求而无需在根元素中嵌套数据。
例如,在一个名为Tag
的模型中,它基本上转换
{
name: "Some name",
age: "Some age"
}
到
{
tag:
{
name: "Some name",
age: "Some age"
}
}
但是,正如您在测试中看到的那样,如果您将所需的键更改为 not_tag
,则包装会按预期中断 API 调用。
可以使用 config/initializers/wrap_parameters.rb
文件更改此配置。在该文件中,您可以将 wrap_parameters format: [:json]
设置为 wrap_parameters format: []
以禁止此类参数包装。