web-dev-qa-db-fra.com

Rails 4 - Répondre uniquement au JSON et non au HTML

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.

33
Tom Brunoli

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  
34
csi

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 }
30
Chance Feick

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 .

13
Sia

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

5
Franzé Jr.

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
3
A H K

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
2
Shubham Abrol

Vous pouvez le définir en ayant un filtre avant qui définit explicitement la demande en JSON.

request.format = :json

1
Piotr Usewicz

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
0
Ezequiel García

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
0
7urkm3n