I have a Rails 4 app (that was upgraded from Rails 3) in which I decided to delete one of the controllers. I then moved the methods from that deleted controller to the ApplicationController, which included before_filter :authenticate_user!
Here's what my ApplicationController looks like now:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!
respond_to :json
def index
gon.rabl
#user = current_user
gon.rabl "app/views/users/show.json.rabl", as: "current_user"
end
def markdown
require 'redcarpet'
renderer = Redcarpet::Render::HTML.new
extensions = {}
Redcarpet::Markdown.new(renderer, extensions)
end
helper_method :markdown
end
Now, I'm getting this error:
ActionController::UnknownFormat in Devise::SessionsController#new
I think this might be due to the fact that you have set your application controller to respond only to json. If your Devise Controller inherits from ApplicationController (I think this is the default), then it will expect to see a content-type: json header, or your urls must all end in .json
You shouldn't have the index method defined in application_controller. You should move it to the appropriate controller. If this is something you want to do before every action you might want to try something like this:
before_action :gon_user, only: :index
private
def gon_user
gon.rabl
#user = current_user
gon.rabl "app/views/users/show.json.rabl", as: "current_user"
end
Though i've to be honest that i'm not sure about the gon stuff, can't remember if it was for moving data from ruby to javascript or for responding to ajax/json request.
Thanks Slicedpan. Got me thinking about a
respond_to :json
Used in my Rails Application as an API with Angular. As in my Rails controllers I use for requests from my Angular Services.
respond_with
In my case I ended up adding html to the respond_to:
respond_to :json, :html
The Default Mime Types can be seen here:
http://apidock.com/rails/Mime
Related
I have a single-page application written in React with Ruby on Rails back-end (API mode). Rails is also serving static files. I'm pointing Rails router to public/index.html, so my SPA could manage his own routing with react-router. This is common practice in order to make direct links and refresh to work.
routes.rb
match '*all', to: 'application#index', via: [:get]
application_controller.rb
class ApplicationController < ActionController::API
def index
render file: 'public/index.html'
end
end
The problem is this doesn't work in API mode. It's just an empty response. If I change the parent class to ActionController::Base everything works as expected. But I don't want to inherit the bloat of full class, I need slim API version.
I've tried adding modules like ActionController::Renderers::All and AbstractController::Rendering without success.
If I change the parent class to ActionController::Base everything works as expected. But I don't want to inherit the bloat of full class, I need slim API version.
Yes, if you serve index from ApplicationController, changing its base class would affect all other controllers. This is not good. But what if you had a specialized controller to serve this page?
class StaticPagesController < ActionController::Base
def index
render file: 'public/index.html'
end
end
This way, you have only one "bloated" controller and the others remain slim and fast.
You could do
render text: File.read(Rails.root.join('public', 'index.html')), layout: false
I usually just redirect_to 'file path'.
def export
# When the route coming to get 'some_report/export', to: 'greate_controller#export'
# The file where you write or preparing, then you can redirect some path like : http://localhost:3000/public/tmpfile/report20210507.xlsx
# And it will just redirect the file for you
file_path = "public/tmpfile/report20210507.xlsx"
redirect_to "#{root_url}#{file_path}"
end
For this example
root_url = "http://localhost:3000/"
This should work, and allow you to keep inheriting from ActionController::API--
class ApplicationController < ActionController::API
def index
respond_to do |format|
format.html { render body: Rails.root.join('public/index.html').read }
end
end
end
The render logic changed for ActionController::API with Rails 5.
Im implementing a Rest API on Ruby on Rails. So i want to respond to all requests in json format. I made this:
include ActionController::MimeResponds
before_filter :force_json
def force_json
response.format = "json"
#also tried
# response.content_type = Mime[:json]
end
Those two ways didn't worked. It gives me an html page with errors.
Also is there a way to implement this for the whole api and not for each class?
Thanks!
If you want it to happen application wide, you can do something like this in the application controller.
class ApplicationController < ActionController::Base
before_action :force_json
def force_json
request.format = :json
end
end
If you use the responders gem, you can define this at the top of your class:
class PostsController < ApplicationController
respond_to :json
...
Then this controller will respond using JSON by default.
I am working on a simple API for a rails project which should also be able for versioning.
I am using rabl-rails gem.
To prevent code duplication, I wanna be able to use my ControllerActions (ex. UserController#search) twice. One time for the normal WebUsers, and one for the API.
I only saw people writing controllers like this:
class Api::v1::UsersController
def list
#user = User.all
end
end
Do I have to Namespace Controllers for RABL?
Or is it possible to "route or delegate" my JSON requests to existing Controllers?
For example my regular UsersController Action "list" has actually this:
def list
respond_to do |format|
format.html
format.json { render #user }
end
end
views/users/list.json.rabl also exist and works well in this case.
Now I try to move the rabl files into
/api/v1/users/list.json.rabl
I provide a Route:
namespace :api, :defaults => {:format => :json} do
namespace :v1 do
match 'users/list', to: 'Users#list', as: 'users', via: [:get, :post]
end
end
At the moment I do not provide a Api::V1::UsersController.
What is the best approach to
provide a /api/v1/users/list route but
use the regular UsersController and
have the list.json.rabl view in the /api/v1/ folder?
I hope it's not to complicated explained...
I ended up with the idea, that an API should have his own logic. Some actions have view specific stuff (breadcrumbs, dropdown values, etc.) inside.
With my knowledge "search logic" can be extracted into a module which gets mixed into your ApplicationController class (inlcude). Then you can use this logic in both places: Api and regular web view.
My first attempt is here:
BaseController
# Base Controller for all Api requests
class Api::V1::BaseController < ApplicationController
respond_to :json
acts_as_token_authentication_handler_for User
end
Api SubControllers
class Api::V1::UsersController < Api::V1::BaseController
# Inherit from BaseController -------^^^^^^^^^^^^^^^^^^^^^^^
# your personal extracted logic
include SearchLogic
def list
#user = User.all
end
end
So
In my rails app which is json only, I want to send a 406 code whenever someone calls my rails app with accept header set to anything except application/json. I also want it to send a 415 when I get the content type set to anything except application / json
My controllers have respond_to :json put on them. I only render json in all actions. However how do I ensure that I return error code 406/415 for all calls for anything that is called for all other accept headers/content-type and with format set to anything except json.
Eg. If I my resource is books/1 I want to allow
books/1.json or books/1 with application/json in accept header and content type
Any ideas on how I can do these two actions?
Basically, you can limit your responses in two ways.
First, there is respond_to for your controllers. This would automatically trigger a 406 Not Acceptable if a request for a format is made which is not defined.
Example:
class SomeController < ApplicationController
respond_to :json
def show
#record = Record.find params[:id]
respond_with #record
end
end
The other way would be to add a before_filter to check for the format and react accordingly.
Example:
class ApplicationController < ActionController::Base
before_filter :check_format
def check_format
render :nothing => true, :status => 406 unless params[:format] == 'json' || request.headers["Accept"] =~ /json/
end
end
You can do it with a before_filter in ApplicationController
before_filter :ensure_json_request
def ensure_json_request
return if params[:format] == "json" || request.headers["Accept"] =~ /json/
render :nothing => true, :status => 406
end
On rails 4.2+ respond_to has been removed, so unless you want to import the full responders gem just for this, your best bet is to roll your own. This is what I'm using in my rails 5 api:
class ApplicationController < ActionController::API
before_action :force_json
private
def force_json
# if params[_json] it means request was parsed as json
# if body.read.blank? there was no body (GET/DELETE) so content-type was meaningless anyway
head :not_acceptable unless params['_json'] || request.body.read.blank?
end
end
I was under the impression that Rails will regenerate the form_authenticity_token after any POST, PUT, or DELETE action. But for some reason, after a successful POST to the users resource the form_authenticity_token does not regenerate. I'm free to POST as many time as I would like with the same CSRF token over and over.
I've got a namespaced API and I'm using the RABL gem to build out my responses. This is how I have everything setup...
class Api::V1::UsersController < Api::V1::ApplicationController
...
def create
#user = User.new(params[:user])
render "show", :status => (#user.save ? :ok : :unprocessable_entity)
end
...
end
class Api::V1::ApplicationController < ApplicationController
layout '/api/v1/layouts/application.json.erb'
respond_to :json
before_filter :authenticate_user!
...
end
class ApplicationController < ActionController::Base
protect_from_forgery
end
The post goes through fine, there are no errors or warning in the development.log or in the consoled $ rails s log.
I've check verified_request? from within the create method and it's returning true. I've removed the render and setup a create.json.rabl view with the same code as the show.json.rabl view... no dice.
I'm running Rails 3.1.3 w/ Ruby 1.9.2p290 w/ a cookie session store.
The authenticity token is being sent via request header (X-CSRF-Token)
You were under the wrong impressions. As you can see from the relevant bit of the rails source the form authenticity token stays the same for the lifetime of the session.