问题
有很多指南可用于处理苦艾酒中的错误元组,但对于异常情况几乎为零。
这很重要,因为总是有不可预见的问题可能引发异常并返回不符合graphql响应/错误规范的响应。当像apollo这样的GraphQL 客户端自动批处理请求时,这尤其成问题,并且一个查询中的异常将导致整个 BEAM Web 进程崩溃,从而导致所有查询失败。
现有方法
我的第一个想法是使用中间件将解析器包装在一个try/rescue块中,我遇到的仅有的两个链接也提出了类似的方法:
Elixir 论坛: 如何使用 Absinthe.MiddleWare 捕获异常?
Absinthe 的创建者之一 Ben Wilson 建议用自定义的中间件替换中间件,该中间件在块
Resolution中执行解析器try这不会处理其他中间件中的异常(但也许应该是这样)
博客文章: 使用中间件处理苦艾酒中的 Elixir 异常
- 尝试做同样的事情,但不遵循
Absinthe.Middleware行为规范 - 而是将所有现有的中间件包装在匿名函数中
- 因此,我们在检查启用的中间件及其配置时也会失去洞察力
- 尝试做同样的事情,但不遵循
我的解决方案
我的方法从博客文章中得到了一些启发,但我尝试遵循这种行为并使用中间件元组规范而不是匿名函数:
中间件定义:
defmodule MyApp.ExceptionMiddleware do
@behaviour Absinthe.Middleware
@default_error {:error, :internal_server_error}
@default_config []
@spec wrap(Absinthe.Middleware.spec()) :: Absinthe.Middleware.spec()
def wrap(middleware_spec) do
{__MODULE__, [handle: middleware_spec]}
end
@impl true
def call(resolution, handle: middleware_spec) do
execute(middleware_spec, resolution)
rescue
error ->
Sentry.capture_exception(error, __STACKTRACE__)
Absinthe.Resolution.put_result(resolution, @default_error)
end
# Handle all the ways middleware can be defined
defp execute({{module, function}, config}, resolution) do
apply(module, function, [resolution, config])
end
defp execute({module, config}, resolution) do
apply(module, :call, [resolution, config])
end
defp execute(module, resolution) when is_atom(module) do
apply(module, :call, [resolution, @default_config])
end
defp execute(fun, resolution) when is_function(fun, 2) do
fun.(resolution, @default_config)
end
end
在架构中应用它:
在所有查询/变异中间件上调用该wrap/1方法
def middleware(middleware, _field, %{identifier: type}) when type in [:query, :mutation] do
Enum.map(middleware, &ExceptionMiddleware.wrap/1)
end
结果:
这将它们转换为:
[
{ExceptionMiddleware, handle: {AuthMiddleware, [access: :admin]}},
{ExceptionMiddleware, handle: {{Resolution, :call}, &some_resolver/3}},
{ExceptionMiddleware, handle: {Subscription, []}},
{ExceptionMiddleware, handle: &anon_middleware/2},
]
问题)
我仍然对我的方法没有完全的信心,因为这感觉有点生硬,并且滥用了苦艾酒的中间件。所以,我有兴趣得到几个问题的答案:
- 还有哪些其他可能的方法?毕竟使用 Absinthe 中间件是正确的选择吗?
- 如果是这样,包装所有中间件或仅替换中间件是否有意义
Absinthe.Resolution? - 这样做的规范方法是什么?