javascript - 如何从 Rails View 调用内部 API(用于 ReactJS 预渲染目的)?

标签 javascript ruby-on-rails reactjs react-rails

我已经有了返回 JSON 响应的 Rails API Controller 。前端 Javascript(以及移动应用程序)使用它来呈现值。

现在,我希望使用 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 请求)?

最佳答案

我同意您希望为您的 View 重用您的 API 代码。这将使应用程序更易于维护。

如果稍微改变一下范围会怎样?不是调用 Controller 方法,而是将逻辑移到新的 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 Controller 中再次使用序列化程序:

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

因为您在这两种情况下使用了相同的序列化程序,所以产品的 JSON 表示形式将相同!

这种方法有几个优点:

  • 因为你的序列化器是一个普通的 Ruby 类,所以很容易编写和测试
  • 在 Controller 之间共享 JSON 逻辑很容易
  • 它的可扩展性很强:当您出于不同的目的需要 JSON 时,只需添加一个新的序列化器并使用它即可。

有些人使用ActiveModel Serializers为此,但我没有。我在一年前尝试过 AMS,但我不喜欢它,因为它覆盖了你应用中所有对象的 as_json,这导致我的案例发生重大变化!

关于javascript - 如何从 Rails View 调用内部 API(用于 ReactJS 预渲染目的)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32843005/

相关文章:

javascript - Keystonejs:自定义 Keystonejs 默认管理界面主题

javascript - JS中重置多个定时器

sql - 使用内连接的 Rails ActiveRecord 查询

reactjs - 使用 URL 对象进行 Jest 测试

javascript - 当渲染返回另一个组件时,React 不会调用 componentWillMount

javascript - firebase.database.ref 不是函数错误

javascript - 检查了所有单选按钮,然后获取各自行的 "first td"值,然后在单击提交后立即将所有第一个 td 的值放入 url 中

ruby-on-rails - 在 Rails 模型中编写正则表达式查询

ruby-on-rails - 用 Thin 替换 WEBrick 时要知道的事情

javascript - 将传递状态作为 react-native-tab-view 的 Prop 的问题?