Simple respond_with in rails that avoids 204 from PUT

Ruby on-Rails

Ruby on-Rails Problem Overview


I want to PUT to rails and avoid getting a 204. I am using this pattern:

class SomeController < ApplicationController
  respond_to :json

  def update
    # ...
    respond_with(some_object)
  end
end

However, when I do a put to update, I get a 204 back. I realize this is completely valid etc, but I explicitly want the content back. I can override it to some extent like this:

def update
  respond_with(some_object) do |format|
    format.json{render json: some_object}
  end
end

but this seems a bit too hands-on for rails. Is there any more idiomatic way of avoiding a 204 and requesting the full content to be sent back? This is Rails 3.2.

In summary: I want maximally idiomatic rails that avoids a 204.

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

I made a custom responder which always returns my JSON encoded resource even on PUT/POST.

I put this file in lib/responders/json_responder.rb. Your /lib dir should be autoloaded.

module Responders::JsonResponder
  protected

  # simply render the resource even on POST instead of redirecting for ajax
  def api_behavior(error)
    if post?
      display resource, :status => :created
    # render resource instead of 204 no content
    elsif put?
      display resource, :status => :ok
    else
      super
    end
  end
end

Now, explicitly modify the controller which requires this behavior, or place it in the application controller.

class ApplicationController < ActionController::Base

  protect_from_forgery

  responders :json

end

You should now get JSON encoded resources back on PUT.

Solution 2 - Ruby on-Rails

As a less invasive alternative, you can pass a json: option to the respond_with method invocation inside your controller update action, like this:

def update
  # ...
  respond_with some_object, json: some_object
end

Granted it seems a bit unDRY having to repeat the object twice in the arguments, but it'll give you what you want, the json representation of the object in the response of a PUT request, and you don't need to use the render json: way, which won't give you the benefits of responders.

However, if you have a lot of controllers with this situation, then customizing the responders, as jpfuentes2 showed in the accepted anwser, is the way to go. But for a quick single case, this alternative may be easier.

Source: https://github.com/plataformatec/responders/pull/115#issuecomment-72517532

Solution 3 - Ruby on-Rails

This behavior seems intentional to fall in line with the HTTP spec, and "ideally" you should be firing off an additional GET request to see the results. However, I agree in the real world I'd rather have it return the JSON.

@jpfuentes2's solution above should do the trick (it's very similar to the pull request below), but I'm hesitant to apply anything that's patching rails internals, as it could be a real pain to upgrade between major versions, especially if you don't have tests for it (and let's face it, developers often skimp on controller tests).

References

Solution 4 - Ruby on-Rails

Just to clarify, you do not need the responders gem to do this... You can just do:

config/initializers/responder_with_put_content.rb

class ResponderWithPutContent < ActionController::Responder
  def api_behavior(*args, &block)
    if put?
      display resource, :status => :ok
    else
      super
    end
  end
end

and then either (for all updates actions to be affected):

class ApplicationController < ActionController::Base
  def self.responder
    ResponderWithPutContent
  end
end

or in your action:

def update
  foo = Foo.find(params[:id])
  foo.update_attributes(params[:foo])
  respond_with foo, responder: ResponderWithPutContent
end

Solution 5 - Ruby on-Rails

What's wrong with simply doing:

def update
  some_object = SomeObject.update()
  render json: some_object
end

Solution 6 - Ruby on-Rails

Not a big fan of this behavior. To get around it, I had to avoid using the respond_with method:

class SomeController < ApplicationController
  respond_to :json

  def update
    # ...
    respond_to do |format|
      format.json { render(json: some_object, status: 200) }
    end
  end
end

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeterView Question on Stackoverflow
Solution 1 - Ruby on-Railsjpfuentes2View Answer on Stackoverflow
Solution 2 - Ruby on-RailsErnestoView Answer on Stackoverflow
Solution 3 - Ruby on-RailsGabe Martin-DempesyView Answer on Stackoverflow
Solution 4 - Ruby on-RailspatrickView Answer on Stackoverflow
Solution 5 - Ruby on-Railstybro0103View Answer on Stackoverflow
Solution 6 - Ruby on-RailsTom RossiView Answer on Stackoverflow