Ruby-on-rails 使用 Rails 3.2.11 和 RSpec 发布原始 JSON 数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14775998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
POSTing raw JSON data with Rails 3.2.11 and RSpec
提问by Daniel Vandersluis
In order to ensure that my application is not vulnerable to this exploit, I am trying to create a controller test in RSpec to cover it. In order to do so, I need to be able to post raw JSON, but I haven't seemed to find a way to do that. In doing some research, I've determined that there at least used to be a way to do so using the RAW_POST_DATAheader, but this doesn't seem to work anymore:
为了确保我的应用程序不易受到此漏洞的攻击,我尝试在 RSpec 中创建一个控制器测试来覆盖它。为此,我需要能够发布原始 JSON,但我似乎还没有找到一种方法来做到这一点。在做一些研究时,我确定至少曾经有一种方法可以使用RAW_POST_DATA标题来做到这一点,但这似乎不再起作用:
it "should not be exploitable by using an integer token value" do
request.env["CONTENT_TYPE"] = "application/json"
request.env["RAW_POST_DATA"] = { token: 0 }.to_json
post :reset_password
end
When I look at the params hash, token is not set at all, and it just contains { "controller" => "user", "action" => "reset_password" }. I get the same results when trying to use XML, or even when trying to just use regular post data, in all cases, it seems to not set it period.
当我查看 params 哈希时,根本没有设置令牌,它只包含{ "controller" => "user", "action" => "reset_password" }. 我在尝试使用 XML 时得到相同的结果,甚至在尝试只使用常规发布数据时,在所有情况下,它似乎都没有设置周期。
I know that with the recent Rails vulnerabilities, the way parameters are hashed was changed, but is there still a way to post raw data through RSpec? Can I somehow directly use Rack::Test::Methods?
我知道随着最近的 Rails 漏洞,参数的散列方式发生了变化,但是还有没有办法通过 RSpec 发布原始数据?我可以以某种方式直接使用Rack::Test::Methods吗?
回答by Daniel Vandersluis
As far as I have been able to tell, sending raw POST data is no longer possible within a controller spec. However, it can be done pretty easily in a request spec:
据我所知,在控制器规范中不再可能发送原始 POST 数据。但是,它可以在请求规范中很容易地完成:
describe "Example", :type => :request do
params = { token: 0 }
post "/user/reset_password", params.to_json, { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
#=> params contains { "controller" => "user", "action" => "reset_password", "token" => 0 }
end
回答by mastaBlasta
This is the way to send raw JSON to a controller action (Rails 3+):
这是将原始 JSON 发送到控制器操作的方式(Rails 3+):
Let's say we have a route like this:
假设我们有这样的路线:
post "/users/:username/posts" => "posts#create"
post "/users/:username/posts" => "posts#create"
And let's say you expect the body to be a json that you read by doing:
假设您希望主体是您通过执行以下操作读取的 json:
JSON.parse(request.body.read)
JSON.parse(request.body.read)
Then your test will look like this:
然后您的测试将如下所示:
it "should create a post from a json body" do
json_payload = '{"message": "My opinion is very important"}'
post :create, json_payload, {format: 'json', username: "larry" }
end
{format: 'json'}is the magic that makes it happen. Additionally, if we look at the source for TestCase#post http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-processyou can see that it takes the first argument after the action (json_payload) and if it is a string it sets that as raw post body, and parses the rest of the args as normal.
{format: 'json'}是让它发生的魔法。此外,如果我们查看 TestCase#post http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-process的源代码,您可以看到它在操作之后采用第一个参数( json_payload),如果它是一个字符串,它会将其设置为原始帖子正文,并正常解析其余的参数。
It's also important to point out that rspec is simply a DSL on top of the Rails testing architecture. The postmethod above is the ActionController::TestCase#post and not some rspec invention.
同样重要的是要指出 rspec 只是 Rails 测试架构之上的一个 DSL。post上面的方法是 ActionController::TestCase#post 而不是一些 rspec 发明。
回答by andrewpurcell
What we've done in our controller tests is explicitly set the RAW_POST_DATA:
我们在控制器测试中所做的是显式设置 RAW_POST_DATA:
before do
@request.env['RAW_POST_DATA'] = payload.to_json
post :my_action
end
回答by Jeremy Lynch
Rails 5 example:
Rails 5 示例:
RSpec.describe "Sessions responds to JSON", :type => :request do
scenario 'with correct authentication' do
params = {id: 1, format: :json}
post "/users/sign_in", params: params.to_json, headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
expect(response.header['Content-Type']).to include 'application/json'
end
end
回答by qqbenq
Here is a full working example of a controller test sending raw json data:
这是发送原始 json 数据的控制器测试的完整工作示例:
describe UsersController, :type => :controller do
describe "#update" do
context 'when resource is found' do
before(:each) do
@user = FactoryGirl.create(:user)
end
it 'updates the resource with valid data' do
@request.headers['Content-Type'] = 'application/vnd.api+json'
old_email = @user.email
new_email = Faker::Internet.email
jsondata =
{
"data" => {
"type" => "users",
"id" => @user.id,
"attributes" => {
"email" => new_email
}
}
}
patch :update, jsondata.to_json, jsondata.merge({:id => old_id})
expect(response.status).to eq(200)
json_response = JSON.parse(response.body)
expect(json_response['data']['id']).to eq(@user.id)
expect(json_response['data']['attributes']['email']).to eq(new_email)
end
end
end
end
The important parts are:
重要的部分是:
@request.headers['Content-Type'] = 'application/vnd.api+json'
and
和
patch :update, jsondata.to_json, jsondata.merge({:id => old_id})
The first makes sure that the content type is correctly set for your request, this is pretty straightforward. The second part was giving me headaches for a few hours, my initial approach was quite a bit different, but it turned out that there is a Rails bug, which prevents us from sending raw post data in functional tests (but allows us in integration tests), and this is an ugly workaround, but it works (on rails 4.1.8 and rspec-rails 3.0.0).
第一个确保为您的请求正确设置了内容类型,这非常简单。第二部分让我头疼了几个小时,我最初的方法有点不同,但结果证明有一个Rails 错误,它阻止我们在功能测试中发送原始数据(但允许我们在集成测试中),这是一个丑陋的解决方法,但它有效(在 rails 4.1.8 和 rspec-rails 3.0.0 上)。

