Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Rails 2.3 JSON "put" request routing is broken

#2448

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.

Reported by Joseph Chen · April 7th, 2009 @ 10:47 PM

State: new
Milestone: 2.3.10
Assigned to: nobody
Importance: none

Activity

  1. David Burger
    David Burger

    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.

    April 17th, 2009 @ 02:39 AM

  2. jonas (at stixy)
    jonas (at stixy)

    Yeah, me too. Is anyone working on this? Or, have anyone found a solution?

    April 23rd, 2009 @ 09:15 AM

  3. Andriy Tyurnikov
    Andriy Tyurnikov

    Did you call rake rails:update after switching to 2.3.2? Please post your error message

    April 26th, 2009 @ 09:41 AM

  4. Joseph Chen
    Joseph Chen

    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:inrecognize' /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:in call'

    April 26th, 2009 @ 05:58 PM

  5. Ryan Bigg
    Ryan Bigg

    Please show the code you are using that is generating the form / whatever that is PUT'ing.

    April 26th, 2009 @ 11:21 PM

  6. Joseph Chen
    Joseph Chen

    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),
    
        });
    
    

    April 27th, 2009 @ 12:27 AM

  7. Joseph Chen
    Joseph Chen

    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.

    May 2nd, 2009 @ 05:46 AM

  8. Joseph Chen
    Joseph Chen

    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.

    May 2nd, 2009 @ 06:08 AM

  9. Zyclops
    Zyclops

    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.

    May 15th, 2009 @ 01:27 AM

  10. Zyclops
    Zyclops

    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'}}
    

    }); });

    
    

    May 15th, 2009 @ 01:30 AM

  11. Joseph Chen
    Joseph Chen

    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'
    }
    

    May 15th, 2009 @ 02:03 AM

  12. Seth Ladd
    Seth Ladd

    For the record, we ran into this bug as well.

    June 8th, 2009 @ 10:00 AM

  13. Laurence A. Lee
    Laurence A. Lee

    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.

    June 8th, 2009 @ 12:22 PM

  14. seamusabshere
    seamusabshere

    This may help:

    module ActionController

    class ParamsParser
      private
        def parse_formatted_parameters(env)
          env['rack.input'].rewind             # <-- add this
          request = Request.new(env)
    

    June 16th, 2009 @ 10:57 PM

  15. lbayes (at patternpark)
    lbayes (at patternpark)

    We are experiencing this issue also.

    June 19th, 2009 @ 01:07 AM

  16. Cameron Westland
    Cameron Westland

    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.

    July 2nd, 2009 @ 09:54 PM

  17. Uwe Mesecke
    Uwe Mesecke
    • 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?

    March 26th, 2010 @ 12:50 PM

  18. Rizwan Reza
    Rizwan Reza
    • Milestone changed from 2.x to 2.3.6

    March 26th, 2010 @ 01:51 PM

  19. eggie5
    eggie5

    i'm seeing this bug too, Joseph Chen's fix works.

    March 30th, 2010 @ 09:48 AM

  20. edraut
    edraut

    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.

    April 6th, 2010 @ 06:36 PM

  21. Neeraj
    Neeraj

    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

    April 19th, 2010 @ 05:32 PM

  22. Rizwan Reza
    Rizwan Reza
    • 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

    May 16th, 2010 @ 02:41 AM

  23. Jeremy Kemper
    Jeremy Kemper
    • Milestone changed from 2.3.6 to 2.3.7

    May 23rd, 2010 @ 05:54 PM

  24. Jeremy Kemper
    Jeremy Kemper
    • Milestone changed from 2.3.7 to 2.3.8

    May 24th, 2010 @ 09:40 AM

  25. Jeremy Kemper
    Jeremy Kemper
    • Milestone changed from 2.3.8 to 2.3.9

    May 25th, 2010 @ 11:45 PM

  26. nicolas_o
    nicolas_o
    • Importance changed from to

    I have this problem too, using rails 2.3.8. The fix works. Any better solution in sight ?

    July 7th, 2010 @ 11:53 AM

  27. Jeremy Kemper
    Jeremy Kemper
    • Milestone changed from 2.3.9 to 2.3.10

    August 30th, 2010 @ 02:28 AM

  28. Dru Jensen
    Dru Jensen

    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'}}
    }); });

    December 27th, 2010 @ 06:31 PM

  29. nicolas_o
    nicolas_o

    The issue is gone with rails 3. I don't need the monkey-patch anymore.

    February 25th, 2011 @ 11:26 AM