2015-09-29 55 views
7

JSONレスポンスを返すRails APIコントローラが既にあります。フロントエンドのJavascript(モバイルアプリだけでなく)が値をレンダリングするために使用されます。内部APIをRailsビューから呼び出す方法(ReactJSプリレンダリング目的)?

今、私は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}) %> 

がどのように効率的に内部/api/v1/products/api/v1/user URLを呼ぶのです(例えば、自分のサーバーにHTTP GETリクエストをすることなく)?私はあなたの意見のためにあなたのAPIのコードを再利用するためにあなたの欲求に同意

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

https://github.com/rails-api/active_model_serializersをご覧ください –

+0

ビューとAPIレイヤーを混在させないと、より効率的になるようです同じサーバーへの呼び出しによってビューが生成されます(特に、要求がクライアントから完全に来ない場合)。他のソリューションは、より複雑で保守性が低いです。 – pherris

答えて

1

はこれを試してみてください。これにより、アプリケーションの保守性が大幅に向上します。

スコープを少し変更した場合はどうなりますか?コントローラメソッドを呼び出す代わりに、ロジックを新しいRubyクラスに移動します。

このクラスの仕事は、オブジェクトをJSON文字列に変換することです。そのため、「シリアライザ」と呼ばれています。私のアプリでは、異なるシリアライザクラスを格納するためにapp/serializers/{model_name}/があります。

ここでは例のシリアライザだ:

# 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 

次に、あなたの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 

その後、あなたは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 

同じ両方のケースでerializerを使用すると、製品のJSON表現は同じになります!

このアプローチにはいくつかの利点があります:

  • は、あなたのシリアライザは、プレーンなRubyのクラスなので、それは&テスト
  • を書くのは簡単だ、それはだ、コントローラ
  • 間JSONロジックを共有することは簡単です非常に拡張性があります。異なる目的でJSONが必要な場合は、新しいシリアライザを追加して使用するだけです。

これにはActiveModel Serializersを使用している人もいますが、そうではありません。私は1年前にAMSを試しましたが、あなたのアプリのすべてのオブジェクトについてas_jsonを上書きしてしまったので、私はそれが好きではありませんでした。

+0

「Product.all」の部分は複数の複雑な文であることがあります。だから私はコントローラーAPIからコピーしたくない。 –

+0

なぜリダイレクトしないのですか? – dimakura

+0

事前レンダリングに必要な複数の内部API呼び出しがある可能性があります(例:@product_json、@user_json) –

3

+0

Thx。しかし、次のコードはまだ2つのコントローラに複製されています。 " 製品は= Product.all @products_json = products.mapん|製品| シリアライザ=製品:: APISerializer.new(製品、CURRENT_USER) serializer.as_json エンド " 私の現在の実装で は、私がすることにしましたそれらを「関心事」にリファクタリングする。より良い解決策はありますか? –

関連する問題