我正在尝试在rails中创建超媒体api。我想使用json_api适配器使用active_model_serializers序列化我的有效负载。但是有条件地序列化链接似乎并不容易。
这是一种博客应用程序,用户可以关注其他用户。因此,当我序列化用户资源时,例如对于UserA,我希望有一个与rel的链接:如果current_user不跟随UserA,则跟随,如果current_user已经跟随UserA,则关注rel:unfollow。 在创建超媒体API时,这似乎是一个非常简单的用例。有没有人知道使用active_model_serializers有什么好办法吗?
我目前写了这样的东西(并将其包含在所有序列化器中):
def self.link(rel, &block)
serializer = self
super do
user = scope
next unless serializer.can?(user, rel, @object)
instance_eval(&block)
end
end
# And in serializer (just as usual):
link :self do
api_user_path(object.id)
end
确实有效。但它感觉不对。如果将来对active_model_serializers的更改搞砸了,我不会感到惊讶。
答案 0 :(得分:0)
如果其他人正在寻找解决方案,我就是这么做的。我添加了gem Pundit并通过添加名为“link _#{rel}”的方法使Policy类负责链接序列化(以及通常的授权)。我创建了一个这样的基本序列化器:
module Api
class BaseSerializer < ActiveModel::Serializer
include Pundit
def self.link(rel, &block)
unless block_given?
Rails.logger.warn "Link without block (rel '#{rel}'), no authorization check"
return super
end
method = "link_#{rel}"
# We need to let the super class handle the evaluation since
# we don't have the object here in the class method. This block
# will be evalutated with instance_eval in the adapter (which has
# the object to be serialized)
super do
policy_class = PolicyFinder.new(object).policy
unless policy_class
Rails.logger.warn "Could not find policy class for #{object.class}."
next
end
user = scope
policy = policy_class.new(user, object)
unless policy.respond_to?(method)
Rails.logger.warn "Serialization of #{object.class} infers link with rel '#{rel}'. " \
"But no method '#{method}' in #{policy.class}."
next
end
next unless policy.public_send(method)
instance_eval(&block)
end
end
end
end
然后其他序列化程序继承自BaseSerializer,如:
module Api
class UserSerializer < BaseSerializer
type 'user'
attributes :name,
:email,
:followers_count,
:following_count,
:created_at,
:updated_at
link :self do
api_user_url(object)
end
link :edit do
api_user_url(object)
end
link :follow do
follow_api_user_url(object)
end
link :unfollow do
unfollow_api_user_url(object)
end
end
end
因此,策略就像普通的Pundit策略一样,为每个应该序列化(或不是)的链接添加一些方法。
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def link_self
true
end
end
module Api
class UserPolicy < ApplicationPolicy
alias current_user user
alias user record
def link_edit
current_user && current_user.id == user.id
end
# show follow link if user is not current_user and
# current_user is not already following user
def link_follow
current_user && current_user.id != user.id && !current_user.following?(user)
end
# show follow link if user is not current_user and
# current_user is following user
def link_unfollow
current_user && current_user.id != user.id && current_user.following?(user)
end
end
end