5

我知道在使用视图模板(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 对象。

但是,这不会发生,整个序列化程序被忽略。但是,如果我将 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 过滤器的好处(在我看来,我有一些冗余代码)。

这应该怎么做?

4

2 回答 2

0

如果没有明确的renderor 或respond_withrespond_toRails 将寻找匹配的模板。如果该模板不存在,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.)
  • 我只测试了一点。逻辑可能更聪明。

笔记:

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

我们在第二个中看到的“冗余代码”只是这一行:

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

而且我相信这与您的 before_action 逻辑相同。您不需要此行,只需将其删除即可。现在重复被删除了。

到剩下的部分。一个动作需要知道要渲染什么。默认情况下,如果 action 为空或不存在,respond_to则会查找并呈现相应的 'action_name'.html.erb(以及由 指定的其他格式)。

这就是 Rails 4 生成器创建的工作的原因:它创建show.html.erbshow.json.jbuilder渲染。

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

render json: @product

或者

respond_with @product

于 2014-04-25T01:42:17.207 回答