I want to respond with json to all formats.
I can force the render format to json so the action will render show.json despite the accept header:
def show
render formats: :json
end
How I can set render format for all actions of the controller?
Something like this:
class GalleriesController < ApplicationController
formats :json
end
As a summary of all comments to the questions and readability for future users, you can do, as mentioned here:
before_filter :default_format_json
def default_format_json
request.format = "json"
end
In your controller:
def my_action
formats.clear
formats << :json
end
(I've only tested this in Rails 4.2 and 3.2.)
formats returns an Array of format symbols. It is delegated to #_lookup_context, which is an instance of ActionView::LookupContext.
Overwrite the response content type. Read more about the response object here: http://guides.rubyonrails.org/action_controller_overview.html#the-response-object
before_filter :force_json
def force_json
response.content_type = Mime[:json]
end
With respond_to:
def action
respond_to do |format|
format.any(:html, :js, :json) { render json: #object.to_json }
end
end
Related
Is it possible to create an after_filter method in the Rails ApplicationController that runs on every action and renders to JSON? I'm scaffolding out an API, and I'd like to render output to JSON for every action in the controller.
clients_controller.rb
def index
#response = Client.all
end
application_controller.rb
...
after_action :render_json
def render_json
render json: #response
end
The after_action is never executed, and the code aborts with:
Template is missing. Missing template clients/index, ...
If the render json: #response is moved into the controller action, it works correctly.
Is there a filter that will allow me to DRY up the controllers and move the render calls to the base controller?
You can't render after_action/ after_filter. The callback after_action is for doing stuff after rendering. So rendering in after_action is too late.
But your exception is just because you miss the JSON template. I recommend using RABL (which offers a lot of flexibility to your JSON responses and there is also a Railscast about it). Then your controller could look like:
class ClientsController < ApplicationController
def index
#clients = Client.all
end
def show
#client = Client.find params[:id]
end
end
And don't forget to create your rabl templates.
e.g. clients/index.rabl:
collection #clients, :object_root => false
attributes :id
node(:fancy_client_name) { |attribute| attribute.client_method_generating_a_fancy_name }
But in the case you still want to be more declarative you can take advantage of the ActionController::MimeResponds.respond_to like:
class ClientsController < ApplicationController
respond_to :json, :html
def index
#clients = Client.all
respond_with(#clients)
end
def show
#client = Client.find params[:id]
respond_with(#client)
end
end
Btw. beware if you put code in an after_action, this will delay the whole request.
respond_to do |format|
format.html { render :html => #something }
format.json { render :json => #something }
format.xml { render :xml => #something }
end
Here we have three different formats: html, json, xml. So which one is actually returned? Do we have three different files ending with .html, .xml, .json? Or in other words, does respond_to render all three html, json, xml files?
respond_to is a Rails helper method that is attached to the Controller class (or rather, its super class). It is referencing the response that will be sent to the View (which is going to the browser).
The block in your example is formatting data - by passing in a 'format' paramater in the block - to be sent from the controller to the view whenever a browser makes a request for html or json data.
in rails you can write this also
class PostsController < ApplicationController
respond_to :html, :xml, :js
def index
#posts = Post.all
respond_with(#posts)
end
end
respond_to can render each of the three, according to the current request. The right response is not what's returned from respond_to but what's actually rendered.
You can find the full explanation here
Environment
Rails 3.2.6
Ruby 1.9.3p194
What I found
class ThemesController < ApplicationController
def show
end
end
This setting will always render the /views/themes/show.html.erb page no matter what URL extension is. For example:
http://localhost/themes/1.json
http://localhost/themes/1.xxx
http://localhost/themes/1.custom_ext
...
Encounter
I want to run render :json=>#theme when extension is json, otherwise, render the show.html.erb page, so I changed my code:
respond_to do |format|
format.json { render :json => #theme}
format.any {render}
end
It will correctly run render :json=>#theme when URL extension is .json, and render show.html.erb in .xml, .html, etc.
However, I got 406 Not Acceptable in .xxx, .ooo, .custom_ext, and I found it is because only supported MIME type were allowed.
Temporary solution
class ThemesController < ApplicationController
def show
if params[:format].present? && params[:format] == "json"
render :json => #theme
end
end
end
It works fine, and when serving more then 2 formats, such as .xml, .json, .yaml, etc:
class ThemesController < ApplicationController
def show
case params[:format]
when "json" then render :json => #theme
when "xml" then render :xml => #theme
...
else render
end
end
end
It looks clean and not worse than respond_to style :D
Question
I am wondering if there is another better solution?
If case statement can do every thing respond_to can do while respond_to can't, why should I use respond_to?
Quite useful stuff, as it is usually happens, can be found in API docs.
Pay attention to the note there: Note that we used Mime::CSV for the csv mime type as it comes with Rails. For a custom renderer, you’ll need to register a mime type with Mime::Type.register.
You have to put this stuff in config/initializers/mime_types.rb. You'll find there few examples of registering non-default types.
Predefined types are here.
Using RABL in Rails 3.2.x, given the following controller action:
respond_to :html, :json
def create
#foo = Foo.create(params[:foo])
respond_with #foo
end
Assuming the validation fails, how do you get respond_with to use a RABL template instead of the standard JSON hash of errors -- IE. I would like other model attributes besides the validation error message sent back along with the request.
Suggestions?
I found this one out the hard way. You should create a custom responder for your application controller, or at least your individual response. See Three reasons to love ActionController::Responder for more details.
My solution:
# app/responders/api_responder.rb
class ApiResponder < ActionController::Responder
def to_format
case
when has_errors?
controller.response.status = :unprocessable_entity
when post?
controller.response.status = :created
end
default_render
rescue ActionView::MissingTemplate => e
api_behavior(e)
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
#...
self.responder = ApiResponder
#...
end
You could also use respond_with #foo, responder: ApiResponder instead.
Thanks to haxney for sending me in the right direction.
I guess, you need to remove the respond_to call at the top of the controller and remove the respond_with call within the action to get rabl render your rabl template.
Just add a respond_to block at the end of each action where you don't need RABL.
I have this setup:
class UsersController < InheritedResources::Base
respond_to :html, :js, :xml, :json
def index
#users = User.all
respond_with(#users)
end
end
Now I am trying to make it so, if params[:format] =~ /(js|json)/, render :layout => false, :text => #users.to_json. How do I do that with respond_with or respond_to and inherited_resources?
Something like:
def index
#users = User.all
respond_with #users do |format|
format.json { render :layout => false, :text => #users.to_json }
end
end
Assuming you need JSON for an Ajax request
class UsersController < InheritedResources::Base
respond_to :html, :js, :xml, :json
def index
#users = User.all
respond_with(#users, :layout => !request.xhr? )
end
end
This seems like the cleanest solution to me.
Or to prevent you having to hardcode responses for each format in each action.
If you have no layouts for any of the actions in this controller it would be nicer to do:
class UsersController < InheritedResources::Base
respond_to :html, :js, :xml, :json
layout false
def index
#users = User.all
respond_with(#users)
end
end
I love #anthony's solution, but didn't work for me... I had to do:
respond_with(#users) do |format|
format.html { render :layout => !request.xhr? }
end
ps: posting an "answer" instead of a comment because stackoverflow comment formatting and "return key == submit" is infuriating!
I just found this out:
Even if it's JSON, Rails is still looking for a layout. As such, the only layout that it finds, in our case, is application.html.
Solution: Make a JSON layout.
So for instance, if you put an empty application.json.erb with a single = yield inside, next to your HTML one, the HTML layout is bettered by the JSON one. You can even use this to surround your JSON with metadata or things like that.
<%# app/views/layouts/application.json.erb %>
<%= yield %>
No other parameters needed, it automagically works!
Tested in Rails 4 only
class UsersController < InheritedResources::Base
layout -> (controller) { controller.request.xhr? ? false : 'application' }
end
You need to set this on your show action.
def show
render :layout => !request.xhr?
end
:)