J'essaie de créer une API dans Rails 4, et j'ai un problème où Rails renvoie une erreur 500 au lieu d'un 406 lors de l'utilisation de respond_to :json
et en essayant d'accéder à la version html.
Voici un exemple de contrôleur illustrant le problème:
class PostsController < ApplicationController
respond_to :json
def index
@posts = Post.all
end
end
J'ai également une vue jbuilder pour index
qui fonctionne lors de l'accès via JSON. Si j'essaie d'accéder à la route sans l'extension JSON, il tente de charger le modèle HTML (qui n'existe pas) et renvoie une erreur 500, au lieu de simplement rendre JSON ou de renvoyer une erreur 406.
Qu'est-ce qui peut causer cela? Bravo pour toute aide.
Je crois qu'il y a 2 parties ici:
1) json uniquement requêtes dans Rails
2) json uniquement réponses dans Rails
1) Configurez votre contrôleur d'application pour garantir les requêtes json uniquement
# app/controller/application_controller.rb
before_action :ensure_json_request
def ensure_json_request
return if request.format == :json
render :nothing => true, :status => 406
end
2) Configurez vos Rails routes API pour garantir les réponses json uniquement
# config/routes.rb
MyApp::Application.routes.draw do
namespace :api, constraints: { format: 'json' } do
namespace :v1 do
resources :posts
end
end
end
Pour éviter de charger le modèle HTML inexistant, définissez le type de ressource par défaut comme JSON dans config/routes.rb:
resources :posts, :defaults => { :format => :json }
Dans Rails 4, vous devez passer un lambda pour appliquer la contrainte sur un itinéraire.
Malheureusement, cela ne fonctionnera PAS car il essaiera toujours de servir (ou tentera de servir) un modèle html car le format est un paramètre facultatif:
resources :posts, constraints: { format: 'json' }
Cela fonctionne (utilise le lambda):
resources :posts, constraints: lambda { |req| req.format == :json }
Voir la deuxième note (finale) dans cette section du guide Rails .
Comme vous utilisez un before_filter, vous obtiendrez un 406 Not Acceptable si une demande de format n'est pas définie.
Exemple:
class SomeController < ApplicationController
respond_to :json
def show
@record = Record.find params[:id]
respond_with @record
end
end
L'autre façon serait d'ajouter un before_filter pour vérifier le format et réagir en conséquence.
Exemple:
class ApplicationController < ActionController::Base
before_filter :check_format
def check_format
render :nothing => true, :status => 406 unless params[:format] == 'json'
end
end
Mais je pense que vous pouvez simplement le faire:
respond_to do |format|
format.json { render :json => @posts }
end
Plus d'informations: http://guides.rubyonrails.org/layouts_and_rendering.html
constraints
ne fonctionnait pas pour les requêtes POST puis j'ai essayé defaults
cela fonctionne pour tous.
namespace :api, :defaults => { :format => 'json' } do
namespace :v1 do
resources :users do
collection do
get 'profile'
end
end
post 'signup' => 'users#create'
post 'login' => 'user_sessions#create'
end
end
Vous pouvez essayer cela, car j'étais également confronté à ce problème et maintenant il est résolu en utilisant cette solution.
class PostsController < ApplicationController
respond_to :json
def index
@posts = Post.all
render json: @posts
end
end
Vous pouvez le définir en ayant un filtre avant qui définit explicitement la demande en JSON.
request.format = :json
lorsque vous essayez une réponse dans json, c'est parce que vous n'avez besoin que de certaines propriétés, j'ai utilisé cette
@my_model=Model.select(:attributeN, :attributeN......, attributeN)
respond_to do |format|
format.json {
render json: @my_model
}
end
Je vous suggère d'essayer gem 'active_model_serializers'
. C'est vraiment génial et reste propre.
ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, if: Proc.new { |c| c.request.format != 'application/json' }
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
end
Itinéraires:
namespace :api, defaults: { format: :json } do
resource :posts
end
Contrôleur des messages:
def index
render json: Post.all
end