This project is archived and is in readonly mode.
Rails 2.3 JSON "put" request routing is broken
Reported by Joseph Chen | April 7th, 2009 @ 10:47 PM | in 2.3.10
In Rails 2.2, I can perform a request JSON put request by:
1) Use method POST 2) Set Content-Type header to "application/json" 3) JSON-encode post data 4) Make sure to add "_method"="put" to post data
In Rails 2.2, this works fine, and my controller correctly invokes the put handler method.
The great thing with this is that the post data can be highly structured value. For instance:
var jsonPostData = {
"_method": "put",
"hash1": {
"array1" : [{
"key1" : "val"
},{
"key2" : "val2"
}]
},
"stringVal" : "val3"
}
This big structure will automatically be converted into a Ruby hash when it is posted to the controller.
With Rails 2.3.2, this no longer works. An error is thrown when the data is posted.
Note that in Rails 2.3, a request submitted "normally" with Content-Type set to "application/x-www-form-urlencoded" works fine. However, I don't know of a way to convert my highly structured JSON data (example given above) into data that can be submitted in the "normal" fashion. (If someone knows how to do this, please let me know as it would be an acceptable workaround).
As it stands, we're stuck using Ruby 2.2 until this problem is resolved.
Comments and changes to this ticket
-
David Burger April 17th, 2009 @ 02:39 AM
I'm having the same problem. Code that performed a POST with _method="PUT" using Prototype Ajax.Request worked fine under 2.2.2 and now does not route to the update action in Rails 2.3.2.
-
jonas (at stixy) April 23rd, 2009 @ 09:15 AM
Yeah, me too. Is anyone working on this? Or, have anyone found a solution?
-
Andriy Tyurnikov April 26th, 2009 @ 09:41 AM
Did you call rake rails:update after switching to 2.3.2? Please post your error message
-
Joseph Chen April 26th, 2009 @ 05:58 PM
Yes rake rails:update was run.
The error is:
"Only get, put, and delete requests are allowed."
The stack:
/Users/jc/.multiruby/install/1.8.7-p72/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib /action_controller/routing/recognition_optimisation.rb:64:in
recognize_path' /Users/jc/.multiruby/install/1.8.7-p72/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_controller /routing/route_set.rb:437:in
recognize' /Users/jc/.multiruby/install/1.8.7-p72/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_controller /routing/route_set.rb:432:incall'
-
Ryan Bigg April 26th, 2009 @ 11:21 PM
Please show the code you are using that is generating the form / whatever that is PUT'ing.
-
Joseph Chen April 27th, 2009 @ 12:27 AM
The post is being done in Ajax, and we happen to be using the ExtJS library for the Ajax request. As I have mentioned, you can reproduce this simply by doing a POST, passing the form variable "_method" = "put", and setting content-type to "application/json".
var postData = {
'_method': 'put', 'test': { 'name':'test name' }, 'authenticity_token':window._token }
Ext.lib.Ajax.setDefaultPostHeader(false);
Ext.lib.Ajax.initHeader("Content-Type", "application/json; charset=utf-8"); Ext.Ajax.request({ url: url, method: 'POST', params: Ext.util.JSON.encode(postData), });
-
Joseph Chen May 2nd, 2009 @ 05:46 AM
I've stepped through the ActionController routing code and seem to have a fix for the problem.
I've edited the gems/actionpack-2.3.2/lib/action_controller/routing/route_set.rb, line 483.
The problem was that the previous code set the method to request.method and didn't take into the account the 'method' parameter. So I simply change the code to check if the method is a post and the 'method' parameter is provided, then the method should be set to whatever the value for method is.
def extract_request_environment(request)
method = request.method if method == :post if request.env['action_controller.request.request_parameters'].include?('_method') method = request.env['action_controller.request.request_parameters']['_method'].to_sym end end { :method => method } end
I've attached the route_set.rb file.
I'm not sure if this is the best place to be setting the method type, but it seems to fix the problem.
-
Joseph Chen May 2nd, 2009 @ 06:08 AM
I found a bug with the fix I posted above. It turns out that the key 'action_controller.request.request_parameters' is not always present. So we need to check if it exists.
I also extracted the fix out into a file that can be added as config/initializers/rails_action_controller_fix.rb. So the fix simply overrides the default method with the fix.
In addition, I added a check for the Rails version so that the fix is only applied when using Rails 2.3.2.
if RAILS_GEM_VERSION == '2.3.2' module ActionController
module Routing class RouteSet def extract_request_environment(request) method = request.method if method == :post params_key = 'action_controller.request.request_parameters' if request && request.env && request.env.include?(params_key) && request.env[params_key].include?('_method') method = request.env[params_key]['_method'].to_sym end end { :method => method } end end end
end end
I'd appreciate any suggestions if anyone has a better way to implement this fix.
-
Zyclops May 15th, 2009 @ 01:27 AM
Joseph - I implement your fix but could not get it too work. In firebug under the post tab in the xhr request i have {whatever, but after adding your fix in, the params do not show up.
Switching to 2.2 works however.
-
Zyclops May 15th, 2009 @ 01:30 AM
Client side this is what i have to make the request:
@@@javascript
Ext.onReady(function() {
Ext.Ajax.request({url: '/publications/index', method: 'POST', jsonData: {whatever: {hello: 'test'}}
}); });
-
Joseph Chen May 15th, 2009 @ 02:03 AM
Zyclops, you are missing the '_method': 'put' in your jsonData. Your request should look like:
url: '/publications/index', method: 'POST', jsonData: { 'whatever': {'hello': 'test'}, '_method': 'put' }
-
Laurence A. Lee June 8th, 2009 @ 12:22 PM
IIRC, Rails 2.3 is now Rack Based. This may be the same Rack bug I ran into months ago. Here's the quick-fix:
http://github.com/lalee/rack/commit/1f3fa756192cd5a0524f6fa136c71d9...
Hope this helps.
-
seamusabshere June 16th, 2009 @ 10:57 PM
This may help:
module ActionController
class ParamsParser private def parse_formatted_parameters(env) env['rack.input'].rewind # <-- add this request = Request.new(env)
-
Cameron Westland July 2nd, 2009 @ 09:54 PM
It seems like this could be because the ParamsParser rack middleware is converting application/json requests to a single json object… I'm having the problem too.
-
Uwe Mesecke March 26th, 2010 @ 12:50 PM
- Tag changed from 2.3.2, json, put, request, rest, routing to 2.3.2, 2.3.5, json, patch, put, request, rest, routing
This issue is still present in rails 2.3.5. The fix by Joseph Chen (adding the file to config/initializers) fixed this bug for me. Is there any change that a fix for this problem can be added to the next release? The problem is easily reproducable, a fix is present and reported to be working by several people. Are there any other obstacles I am missing?
-
Rizwan Reza March 26th, 2010 @ 01:51 PM
- Milestone changed from 2.x to 2.3.6
-
edraut April 6th, 2010 @ 06:36 PM
I see this in rails 2.3.5, and Joseph Chen's fix did not work, because the Rack::Request object does not have #action_controller. However, this is available:
Rack::Request#params
So I modified Joseph Chen's fix thusly:
module ActionController module Routing class RouteSet def extract_request_environment(request) method = request.method if method == :post params_key = 'action_controller.request.request_parameters' Rails.logger.info("REQUEST::::::::: #{request.env['QUERY_STRING']}") if request && request.params && request.params.include?('_method') method = request.params['_method'].to_sym end if request && request.env && request.env.include?(params_key) && request.env[params_key].include?('_method') method = request.env[params_key]['_method'].to_sym end end Rails.logger.info("METHOD::::::::: #{method}") { :method => method } end end end end
I left the other bit in there in case there's some ambiguity I don't know about where #action_controller may be available in some other case. Can anyone confirm for me that this is necessary?
In any event this works for me now.
-
Neeraj April 19th, 2010 @ 05:32 PM
Given that rails3 is just around the corner how about using -H "X-Http-Method-Override: put" as an extra option to get around this issue.
this works
curl -X POST -d "user[name]=John" -d "_method=PUT" http://localhost:3000/users/1
this does not work
curl -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/users/1.xml -d 'John' -X POST
this works
curl -H "X-Http-Method-Override: put" -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/users/1.xml -d 'John' -X POST -
Rizwan Reza May 16th, 2010 @ 02:41 AM
- Tag changed from 2.3.2, 2.3.5, json, patch, put, request, rest, routing to 2.3.2, 2.3.5, bugmash, json, patch, put, request, rest, routing
-
nicolas_o July 7th, 2010 @ 11:53 AM
- Importance changed from to
I have this problem too, using rails 2.3.8. The fix works. Any better solution in sight ?
-
Dru Jensen December 27th, 2010 @ 06:31 PM
What about setting the method to "PUT" instead of "POST"?
Ext.onReady(function() {
Ext.Ajax.request({
url: '/publications/index',
method: 'PUT',
jsonData: {whatever: {hello: 'test'}}
}); }); -
nicolas_o February 25th, 2011 @ 11:26 AM
The issue is gone with rails 3. I don't need the monkey-patch anymore.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>