0
我在Rails中创建了一个服务对象来封装为Stripe创建计划的业务逻辑(https://stripe.com/)。 对于服务对象是否有任何好的模式来处理参数验证?导轨;验证服务对象中的参数
为了验证:
- 我要检查所有的输入,零或错误的类型。有什么方法可以轻松验证吗?也许是轨道延伸?
这里是一个例子;
# app/services/service.rb
module Service
extend ActiveSupport::Concern
included do
def self.call(*args)
new(*args).call
end
end
end
# app/services/plan/create.rb
class Plan::Create
include Service
attr_reader :params
def initialize(params = {})
@params = params.dup
end
def call
plan = Plan.new(attrs)
return plan unless plan.valid?
begin
external_card_plan_service.create(api_attrs)
rescue Stripe::StripeError => e
plan.errors[:base] << e.message
return plan
end
plan.save
plan.update(is_active: true, activated_at: Time.now.utc)
plan
end
private
def external_card_plan_service
Stripe::Plan
end
def build_data_hash
{
id: params.fetch(:stripe_plan_id)
stripe_plan_id: params.fetch(:stripe_plan_id)
amount: params.fetch(:amount)
currency: params.fetch(:currency)
interval: params.fetch(:interval)
name: params.fetch(:name)
description: params.fetch(:description)
}
end
def attrs
build_data_hash.slice(:stripe_plan_id, :amount, :currency, :interval, :name, :description)
end
def api_attrs
build_data_hash.slice(:id, :amount, :currency, :interval, :name)
end
end
参考文献:http://brewhouse.io/blog/2014/04/30/gourmet-service-objects。
UPDATE
上面的例子是不那么复杂。 但是,如果在服务对象中调用服务对象,那么验证参数会更好。 CreateUser.call(email_address)
class CreateSubscription
def self.call(plan, email_address, token)
user, raw_token = CreateUser.call(email_address)
subscription = Subscription.new(
plan: plan,
user: user
)
begin
stripe_sub = nil
if user.stripe_customer_id.blank?
customer = Stripe::Customer.create(
source: token,
email: user.email,
plan: plan.stripe_id,
)
user.stripe_customer_id = customer.id
user.save!
stripe_sub = customer.subscriptions.first
else
customer = Stripe::Customer.retrieve(user.stripe_customer_id)
stripe_sub = customer.subscriptions.create(
plan: plan.stripe_id
)
end
subscription.stripe_id = stripe_sub.id
subscription.save!
rescue Stripe::StripeError => e
subscription.errors[:base] << e.message
end
subscription
end
end
而且我尝试使用了Virtus(https://github.com/solnic/virtus)。 但是我不知道如何在这种类型的服务对象中使用它。
UPDATE
# api/v1/controller/plans_controller.rb
module Api
module V1
class PlansController < Api::V1::ApiController
before_action :check_type, only: [:create]
def create
@result = Plan::Create.call(plan_info_params)
if @result.errors.blank?
resource = Api::V1::PlanResource.new(@result, nil)
# NOTE: Include all domains created within this routine.
serializer = JSONAPI::ResourceSerializer.new(Api::V1::PlanResource)
json_body = serializer.serialize_to_hash(resource)
render json: json_body, status: 201 # :ok
else
errors = jsonapi_errors(@result)
response = { errors: errors }
render json: response, status: 422 # :unprocessable_entity
end
end
private
def check_type
data_type = 'plans'
unless params.fetch('data', {}).fetch('type', {}) == data_type
render json: { errors: [{ title: 'Unprocessable Entity', detail: "Type must be #{data_type}" }] }, status: 422
end
end
def plan_info_params
params.require(:data).require(:attributes).permit(:stripe_plan_id, :amount, :currency, :interval, :name, :description)
end
end
end
end
感谢您的回答。那么这里Form对象是否正确使用?此外,我不确定Form对象是否会好,因为我们一直在构建API。 – Tosh
@TSH表格用于进一步处理的验证和准备参数。所以是的,表单对象是一个很好的选择,不管它是否适用于API。 –
谢谢!你如何看待virtus +表单对象?我很想知道优点和缺点。 – Tosh