【问题标题】:Rails api how to handle service object exceptions in controllerRails api如何处理控制器中的服务对象异常
【发布时间】:2021-09-01 19:02:03
【问题描述】:

我想在我的 Rails API 中使用服务对象。在我的服务对象中,如果保存成功,我想保存模型并返回 true,但如果保存不成功,那么我想返回 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 中的 Service Object 获取模型错误?

【问题讨论】:

    标签: ruby-on-rails ruby rails-api service-object


    【解决方案1】:

    您可以通过向服务对象提供方法来解决此问题,即使在 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
    

    【讨论】:

    • 我有一个问题 - 为什么我得到默认错误消息(例如内容:'不能为空白')即使我在服务对象中恢复异常 ActiveRecord::RecordInvalid 并通过@model.errors.add(:base, 'diff message') 添加错误消息?
    • 因为您在记录无效时返回,因此您永远不会引发异常。如果您总是想在正常验证错误时引发异常,那么只需删除行 return false unless model.valid?
    【解决方案2】:

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

    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
    

    【讨论】:

    • 它也可以工作 :) 但我不知道为什么我会从像 :content =&gt; 'can't be blank' 这样的模型中得到默认消息,即使我在服务对象中恢复异常 ActiveRecord::RecordInvalid 并通过 @model.errors.add(:base, 'diff message') 添加错误消息?
    • 您混淆了异常和 ActiveModel 验证。例外是语言级别的控制流。验证只是在您调用 .valid 时运行的回调?或者当您保存记录时。 rescue ActiveRecord::RecordInvalid 绝不会阻止将验证错误添加到模型上的错误对象中。
    • 有多种方法可以跳过验证,例如.save(false),但这听起来更像是您应该提出一个单独的问题并仔细描述您想要实现的目标。
    猜你喜欢
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-11
    • 1970-01-01
    相关资源
    最近更新 更多