3
# users_show_controller.rb
class Controllers::Users::Show
  include Hanami::Action

  params do
    required(:id).filled(:str?)
  end

  def call(params)
    result = users_show_interactor(id: params[:id])

    halt 404 if result.failure?
    @user = result.user
  end
end

# users_show_interactor.rb
class Users::Show::Interactor
  include Hanami::Interactor

  expose :user
  def call(:id)
    @user = UserRepository.find_by(:id)
  end
end

我有一个控制器和一个像上面这样的交互器。我正在考虑在控制器上区分 ClientError 和 ServerError 的更好方法。

我认为如果我能处理如下错误,那就太好了。

handle_exeption StandardError => :some_handler

但是,hanami-interactor 包装了自身内部引发的错误,因此控制器通过来自交互器的结果对象接收错误。

我不认为在控制器上重新引发错误是个好方法。

result = some_interactor.call(params)
raise result.error if result.failure

像这样实现错误处理程序怎么样?我知道if语句会很容易增加,所以这种方式并不聪明。

def call(params)
  result = some_interactor.call(params)
  handle_error(result.error) if result.faulure?
end

private

def handle_error(error)
  return handle_client_error(error) if error.is_a?(ClientError)
  return server_error(error) if error.is_a?(ServerError)
end
4

3 回答 3

3

实际上不是面向hanami的方式,但请看一下带有 do notation 的dry-monads。基本思路是可以通过如下方式编写interactor-like处理代码

def some_action
  value_1 = yield step_1
  value_2 = yield step_2(value_1)
  return yield(step_3(value_2))
end 

def step_1
  if condition
    Success(some_value)
  else
    Failure(:some_error_code)
  end
end

def step_2
  if condition
    Success(some_value)
  else
    Failure(:some_error_code_2)
  end
end

然后在控制器中,您可以使用dry-matcher 匹配故障:

matcher.(result) do |m|
  m.success do |v|
    # ok
  end

  m.failure :some_error_code do |v|
    halt 400
  end

  m.failure :some_error_2 do |v|
    halt 422
  end
end

匹配器可以在prepend所有控制器的代码中定义,因此很容易删除代码重复。

于 2018-08-01T21:27:40.433 回答
0

我通常通过在交互器中引发范围错误来进行,然后控制器只需拯救交互器引发的错误并返回适当的状态响应。

交互者:

module Users
  class Delete
    include Tnt::Interactor

    class UserNotFoundError < ApplicationError; end

    def call(report_id)
      deleted = UserRepository.new.delete(report_id)

      fail_with!(UserNotFoundError) unless deleted
    end
  end
end

控制器:

module Api::Controllers::Users
  class Destroy
    include Api::Action
    include Api::Halt

    params do
      required(:id).filled(:str?, :uuid?)
    end

    def call(params)
      halt 422 unless params.valid?

      Users::Delete.new.call(params[:id])
    rescue Users::Delete::UserNotFoundError => e
      halt_with_status_and_error(404, e)
    end
  end
end

fail_with!并且halt_with_status_and_error分别是我的交互器和控制器常用的辅助方法。

# module Api::Halt
def halt_with_status_and_error(status, error = ApplicationError)
  halt status, JSON.generate(
    errors: [{ key: error.key, message: error.message }],
  )
end

# module Tnt::Interactor
def fail_with!(exception)
  @__result.fail!
  raise exception
end
于 2018-08-23T16:04:41.300 回答
0

Hanami 方式是在每个请求处理程序之前验证输入参数。因此,必须始终在操作逻辑之前识别 ClientError。

halt 400 unless params.valid? #halt ClientError
#your code
result = users_show_interactor(id: params[:id])
halt 422 if result.failure? #ServerError
halt 404 unless result.user
@user = result.user
于 2018-07-31T09:10:48.090 回答