Rails api控制器中如何处理服务对象异常

Rails api how to handle service object exceptions in controller

我想在 Rails API 中使用服务对象。在我的服务对象中,如果保存成功,我想保存模型和 return true,但如果保存不成功,我想 return false 并发送错误消息。我的服务对象看起来像这样:

class ModelSaver
  include ActiveModel::Validations
  
  def initialize(params)
    @params = params
  end

  def save_model
    model ||= Model.new(params)

    return false unless model.valid?
    model.save!

  rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid
    model.errors.add(:base, 'here I want default error message')
    false
  end

  private

  attr_reader :params
end

问题是我不知道如何发送错误消息作为响应。当我尝试发送 service_object.errors.messages 时,它向我显示一个空数组。在 Controller 中看起来像这样:

class ModelController < ApplicationController
...
  def create
    service_object = ModelSaver.new(params)

    if service_object.save_model
       render json: service_object
    else
       render json: { errors: service_object.errors.messages }, status: :unprocessable_entity
    end
  end
...
end

那么如何从 Controller 中的服务对象获取模型错误?

您可以通过向您的服务对象提供方法来解决此问题,即使在 save_modelfalse 返回后也允许返回模型或错误。

我会将服务对象更改为

class ModelSaver
  include ActiveModel::Validations

  attr_reader :model

  def initialize(params)
    @params = params
  end

  def errors
    @model.errors
  end

  def save_model
    @model ||= Model.new(params)

    return false unless model.valid?
    @model.save!

  rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid
    @model.errors.add(:base, 'here I want default error message')
    false
  end

  private

  attr_reader :params
end

控制器方法

def create
  service_object = ModelSaver.new(params)

  if service_object.save_model
     render json: service_object.model
  else
     render json: { errors: service_object.errors.messages }, status: :unprocessable_entity
  end
end

我会重构服务对象,使其只委托给底层模型:

class ModelSaver
  attr_reader :model
  delegate :errors, to: :model
  
  def initialize(**params)
    @model = Model.new(**params)
  end

  def save_model
    if model.save
      true
    else
      model.errors.add(:base, 'you forgot to frobnobize the whatchamacallit')
      false
    end
  end
end
class ModelController < ApplicationController
  def create
    service_object = ModelSaver.new(**params)

    if service_object.save_model
       render json: service_object
    else
       render json: { errors: service_object.errors.messages }, status: :unprocessable_entity
    end
  end
end