Rails converts empty arrays into nils in params of the request

后端 未结 6 1737
闹比i
闹比i 2021-02-01 12:20

I have a Backbone model in my app which is not a typical flat object, it\'s a large nested object and we store the nested parts in TEXT columns in a MySQL database.

I wa

相关标签:
6条回答
  • 2021-02-01 12:35

    I ran into similar issue.

    Fixed it by sending empty string as part of the array.

    So ideally your params should like

    {
      name: "foo",
      surname: "bar",
      nested_json: {
        complicated: [""]
      }
    }
    

    So instead of sending empty array I always pass ("") in my request to bypass the deep munging process.

    0 讨论(0)
  • 2021-02-01 12:42

    Looks like this is a known, recently introduced issue: https://github.com/rails/rails/issues/8832

    If you know where the empty array will be you could always params[:...][:...] ||= [] in a before filter.

    Alternatively you could modify your BackBone model's to JSON method, explicitly stringifying the nested_json value using JSON.stringify() before it gets posted and manually parsing it back out using JSON.parse in a before_filter.

    Ugly, but it'll work.

    0 讨论(0)
  • 2021-02-01 12:45

    You can re-parse the parameters on your own, like this:

    class ApiController
      before_filter :fix_json_params    # Rails 4 or earlier
      # before_action :fix_json_params  # Rails 5
    
      [...]
    
      protected
    
      def fix_json_params
        if request.content_type == "application/json"
          @reparsed_params = JSON.parse(request.body.string).with_indifferent_access
        end
      end
    
      private
    
      def params
        @reparsed_params || super
      end
    end
    

    This works by looking for requests with a JSON content-type, re-parsing the request body, and then intercepting the params method to return the re-parsed parameters if they exist.

    0 讨论(0)
  • 2021-02-01 12:45

    I ran into a similar issue and found out that passing an array with an empty string would be processed correctly by Rails, as mentioned above. If you encounter this while submitting a form, you might want to include an empty hidden field that matches the array param :

    <input type="hidden" name="model[attribute_ids][]"/>
    

    When the actual param is empty the controller will always see an array with an empty string, thus keeping the submission stateless.

    0 讨论(0)
  • 2021-02-01 12:53

    After much searching, I discovered that you starting in Rails 4.1 you can skip the deep_munge "feature" completely using

    config.action_dispatch.perform_deep_munge = false
    

    I could not find any documentation, but you can view the introduction of this option here: https://github.com/rails/rails/commit/e8572cf2f94872d81e7145da31d55c6e1b074247

    There is a possible security risk in doing so, documented here: https://groups.google.com/forum/#!topic/rubyonrails-security/t1WFuuQyavI

    0 讨论(0)
  • 2021-02-01 12:58

    Here's (I believe) a reasonable solution that does not involve re-parsing the raw request body. This might not work if your client is POSTing form data but in my case I'm POSTing JSON.

    in application_controller.rb:

      # replace nil child params with empty list so updates occur correctly
      def fix_empty_child_params resource, attrs
        attrs.each do |attr|
          params[resource][attr] = [] if params[resource].include? attr and params[resource][attr].nil?
        end
      end
    

    Then in your controller....

    before_action :fix_empty_child_params, only: [:update]
    
    def fix_empty_child_params
      super :user, [:child_ids, :foobar_ids]
    end
    

    I ran into this and in my situation, if a POSTed resource contains either child_ids: [] or child_ids: nil I want that update to mean "remove all children." If the client intends not to update the child_ids list then it should not be sent in the POST body, in which case params[:resource].include? attr will be false and the request params will be unaltered.

    0 讨论(0)
提交回复
热议问题