【问题标题】:Does ActiveModel::Serializer require an explicit render call?ActiveModel::Serializer 是否需要显式渲染调用?
【发布时间】:2013-07-11 13:23:06
【问题描述】:

我知道,当使用视图模板(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 过滤器的好处(在我看来,我有一些冗余代码)。

这应该怎么做?

【问题讨论】:

  • @zmilojko 你试过使用respond_with吗?我认为respond_with(@product) 会让你接近你想要的。 Example from ActiveModel::Serializer README.
  • @PaulFioravanti 但这不是我所追求的。我希望 show 方法在 Rails4 生成器创建它时保持为空,但仍然能够使用问题中定义的序列化程序(而不是 jbuilder,因为它似乎是 Rails 更喜欢的)。
  • @zmilojko 这是一个直接的 Rails 4.1 应用程序吗?还是一个 rails-api 应用程序?您是如何创建应用的初始状态的?

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


【解决方案1】:

如果没有明确的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。)
  • 我只对此进行了一点测试。逻辑可能更聪明。

注意事项:

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

【讨论】:

  • 我重新利用了 Jose Valim 的《Crafting Rails》一书中的一些代码来创建这个答案。
  • 嗯,我一直在寻找一种解决方案来简化我的代码,这恰恰相反。我猜 jbuilder 方法会赢,而 ActiveModel:Serializer 只是没那么有用......
  • 你可以这么说。但它只需要执行一次。
  • 我无法使用 Rails 4.1、rails-api 或 active_model_serializers 重现问题生成的输出。如果没有明确的render json:,我也找不到任何 json 示例。您在使用其他宝石吗?我只会在没有显式渲染的情况下收到错误。如果我符合您的设置,我可能会为您提供更好的答案。
【解决方案2】:

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

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

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

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

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

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

render json: @product

respond_with @product

【讨论】:

  • 但我的目标不是删除重复的行,而是删除其余部分。我希望“显示”方法保持为空并仍然调用序列化程序。这似乎不起作用,使整个 ActiveModel:Serializer 无用。
猜你喜欢
  • 2014-01-15
  • 2015-08-05
  • 1970-01-01
  • 2018-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多