2015-09-29 8 views
7

Ho già un controller API Rails che restituisce la risposta JSON. Viene utilizzato dal Javascript front-end (così come dall'app mobile) per il rendering dei valori.Come chiamare l'API interna dalla vista Rails (per lo scopo del prerender di ReactJS)?

Ora, desidero pre-rendering quei valori utilizzando ReactJS:

#app/controllers/api/v1/products_controller.rb 
module API 
    module V1 
     class ProductsController < ApplicationController 
      def index 
       @products = Product.all #this could be acomplex multi-line statements. 
       #rendered in api/v1/products/index.json.jbuilder 
      end 
     end 
    end 
end 

#app/controllers/products_controller.rb 
class ProductsController < ApplicationController 
    def index 
     #How to do this efficiently? 
     @products_json = #Call to internal /api/v1/products/index for prerender purpose. 
     @user_json = #Call to internal /api/v1/user/show for prerender purpose. 
    end 
end 

#app/views/products/index.html.erb 
<%= react_component('ProductsList', @products_json, {prerender: true}) %> 
<%= react_component('UserProfile', @user_json, {prerender: true}) %> 

Come chiamo interno /api/v1/products e /api/v1/user URL in modo efficiente (ad esempio senza fare HTTP GET richiesta al mio server)?

+0

Dai un'occhiata a https://github.com/rails-api/active_model_serializers –

+0

Sembra che sarà più "efficiente" se non mischi la vista e il livello API - niente di sbagliato nel fare due API chiama allo stesso server per popolare la tua vista (specialmente se la richiesta non arriva dal client). Le altre soluzioni sono più complesse e meno manutenibili. – pherris

risposta

1

Prova questa:

def index 
    @products = Product.all 
    @products_json render_to_string('/api/v1/products/index', formats: [:json]) 
    # etc... 
end 
+0

Il fatto è che la parte 'Product.all' può essere più istruzioni complesse. Quindi non voglio copiarlo dall'API del controller. –

+0

Allora perché non semplicemente reindirizzare? – dimakura

+0

Potrebbero essere necessarie più chiamate API interne al prerender (ad [email protected]_json, @user_json) –

3

Sono d'accordo con il vostro desiderio di riutilizzare il codice API per le vostre opinioni. Ciò renderà l'applicazione molto più gestibile.

Cosa succede se si modifica un po 'lo scope? Invece di chiamare un metodo controller, sposta la logica in una nuova classe Ruby.

Il compito di questa classe è trasformare un oggetto in una stringa JSON, quindi è chiamato "serializzatore". Nella mia app, abbiamo app/serializers/{model_name}/ per l'archiviazione di diverse classi di serializzazione.

Ecco un esempio di serializzazione:

# app/serializers/product/api_serializer.rb 
class Product::APISerializer 
    attr_reader :product, :current_user 

    def initialize(product, current_user) 
    @product = product 
    @current_user = current_user 
    end 

    # Return a hash representation for your object 
    def as_json(options={}) # Rails uses this API 
    { 
     name: product.name, 
     description: product.description, 
     price: localized_price, 
     categories: product.categories.map { |c| serialize_category(c) }, 
     # ... all your JSON values 
    } 
    end 

    private 

    # For example, you can put logic in private methods of this class. 
    def localized_price 
    current_currency = current_user.currency 
    product.price.convert_to(current_currency) 
    end 

    def serialize_category(category) 
    { name: category.name } 
    end 
end 

Quindi, utilizzare questo serializzatore per costruire la vostra risposta API:

class API::V1::ProductsController < ApplicationController 
    def index 
    products = Product.all 
    products_json = products.map do |product| 
     serializer = Product::APISerializer.new(product, current_user) 
     serializer.as_json 
    end 
    render json: products_json 
    end 
end 

Quindi, è possibile utilizzare il serializzatore nuovo nel controller UI:

class ProductsController < ApplicationController 
    def index 
    products = Product.all 
    @products_json = products.map do |product| 
     serializer = Product::APISerializer.new(product, current_user) 
     serializer.as_json 
    end 
    # render view ... 
    end 
end 

Perché hai usato la stessa s erializer in entrambi i casi, la rappresentazione JSON dei prodotti sarà la stessa!

Ci sono alcuni vantaggi di questo approccio:

  • Perché il vostro serializzatore è una classe di Ruby pianura, è facile scrivere & prova
  • E 'facile condividere la logica JSON tra controllori
  • E' molto estendibile: quando hai bisogno di JSON per uno scopo diverso, aggiungi semplicemente un nuovo serializzatore e usalo.

Alcune persone usano ActiveModel Serializers per questo, ma io no. Ho provato AMS un anno fa e non mi piaceva perché sovrascrive lo as_json per tutti gli oggetti nella tua app, il che ha causato cambiamenti improvvisi nel mio caso!

+0

Thx per la spiegazione. Il seguente codice viene comunque duplicato in due controller. " prodotti = Product.all @products_json = products.map do | prodotto | serializzatore = prodotto :: APISerializer.new (prodotto, current_user) serializer.as_json fine " Nel mio attuale implementazione, ho deciso di riordinarli in 'preoccupazione'. Qualunque soluzione migliore potrebbe essere? –

Problemi correlati