ActiveModel :: Serializer是否需要显式渲染调用?

时间:2013-07-11 13:23:06

标签: ruby-on-rails-4 actioncontroller active-model-serializers

我知道在使用视图模板(html,rabl)时,我的控制器操作中不需要显式渲染调用,因为默认情况下,Rails会使用与控制器操作名称对应的名称呈现模板。我喜欢这个概念(不关心我的控制器代码中的渲染),因此想知道在使用ActiveModel :: Serializers时这是否可行?

示例,这是来自生成的控制器(Rails 4.1.0)的代码:

class ProductsController < ApplicationController
  before_action :set_product, only: [:show, :edit, :update, :destroy]

  #other actions
  # GET /products/1
  # GET /products/1.json
  def show
  end
end

这是序列化器:

class ProductSerializer < ActiveModel::Serializer
  attributes :id, :name, :description, :url, :quantity, :price
end

点击/products/1.json,我希望会发生两件事:

  1. 未列出序列化程序中未列出的字段
  2. 要在“产品”顶级字段中封装的整个JSON对象。
  3. 然而,这不会发生,整个序列化器被忽略。但是,如果我将Show方法修改为以下内容:

    # GET /products/1
    # GET /products/1.json
    def show
      @product = Product.find(params[:id])
      respond_to do |format|
        format.html
        format.json { render json: @product }
      end
    end
    

    现在一切都很好,但我已经失去了before_action过滤器的好处(在我看来,我有一些冗余代码)。

    这应该怎么做?

2 个答案:

答案 0 :(得分:0)

&#39;冗余代码&#39;我们在第二个看到的只是这一行:

@product = Product.find(params[:id])

我相信这与你之前的行动是一样的逻辑。你不需要这条线,只需删除它。现在删除了重复。

剩下的部分。动作需要知道要渲染的内容。默认情况下,如果操作为空或不存在,则会查找并呈现相应的&#39; action_name&#39; .html.erb(以及respond_to指定的其他格式)。

这就是Rails 4生成器创建的原因:它创建了呈现的show.html.erbshow.json.jbuilder

使用ActiveModel::Serializer,您没有模板。如果将操作留空,则不知道要渲染的内容。因此,您需要通过以下任一方式告诉它将@product呈现为json:

render json: @product

respond_with @product

答案 1 :(得分:0)

如果没有明确的renderrespond_withrespond_to,Rails会查找匹配的模板。如果该模板不存在,Rails会抛出错误。

但是,您可以创建自己的解析器来绕过它。例如,假设您创建了app\models\serialize_resolver.rb并将其放入其中:

class SerializeResolver < ActionView::Resolver
  protected
  def find_templates(name, prefix, partial, details)
    if details[:formats].to_a.include?(:json) && prefix !~ /layout/
      instance = prefix.to_s.singularize
      source = "<%= @#{instance}.active_model_serializer.new(@#{instance}).to_json.html_safe %>"
      identifier = "SerializeResolver - #{prefix} - #{name}"
      handler = ActionView::Template.registered_template_handler(:erb)
      details = {
        format: Mime[:json],
        updated_at: Date.today,
        virtual_path: "/#{normalize_path(name, prefix)}"
      }
      [ActionView::Template.new(source, identifier, handler, details)]
    else
      []
    end
  end

  def normalize_path(name, prefix)
    prefix.present? ? "#{prefix}/#{name}" : name
  end
end

然后,在您的应用程序控制器(或单个控制器)中:

  append_view_path ::SerializeResolver.new

有了这个,你应该能够做你想做的事。如果是json请求,它将创建一个包含正确内容的erb模板并将其返回。

限制:

  • 这有点笨重,因为它依赖于erb,这是不需要的。如果我有时间,我将创建一个简单的模板处理程序。然后我们可以在没有erb的情况下调用它。
  • 这会消除默认的json响应。
  • 它依赖于控制器名称来查找实例变量(/posts转换为@post。)
  • 我只测试了一下这个。逻辑可能更聪明。

注意:

  • 如果存在模板,将首先使用它。这允许您覆盖此行为。
  • 你不能简单地创建一个新的渲染器并注册它,因为默认进程没有命中它。如果找不到模板,则会出现错误。如果找到该文件,则直接调用模板处理程序。