This project is archived and is in readonly mode.

#2448 new
Joseph Chen

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

    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)

    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

    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

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

  • Ryan Bigg

    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

    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

    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

    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

    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

    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

    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'
    }
    
  • Seth Ladd

    Seth Ladd June 8th, 2009 @ 10:00 AM

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

  • Laurence A. Lee

    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

    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)
    
  • lbayes (at patternpark)

    lbayes (at patternpark) June 19th, 2009 @ 01:07 AM

    We are experiencing this issue also.

  • Cameron Westland

    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

    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

    Rizwan Reza March 26th, 2010 @ 01:51 PM

    • Milestone changed from 2.x to 2.3.6
  • eggie5

    eggie5 March 30th, 2010 @ 09:48 AM

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

  • edraut

    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

    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

    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
  • Jeremy Kemper

    Jeremy Kemper May 23rd, 2010 @ 05:54 PM

    • Milestone changed from 2.3.6 to 2.3.7
  • Jeremy Kemper

    Jeremy Kemper May 24th, 2010 @ 09:40 AM

    • Milestone changed from 2.3.7 to 2.3.8
  • Jeremy Kemper

    Jeremy Kemper May 25th, 2010 @ 11:45 PM

    • Milestone changed from 2.3.8 to 2.3.9
  • nicolas_o

    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 ?

  • Jeremy Kemper

    Jeremy Kemper August 30th, 2010 @ 02:28 AM

    • Milestone changed from 2.3.9 to 2.3.10
  • Dru Jensen

    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

    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>

Attachments

Pages