Skip to content

choice-form/ut_api_error

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UtApiError

Choiceform 的 REST API 处理错误的标准库。

本地开发

本项目的必备依赖只有 jason ,其他都是可选的。

测试:

mix test

其他项目集成

下面以 Phoenix 项目举例子,但 ut_api_error 并不依赖任何 web 框架,集成的整体思路也是一致的。

mix.exs 中添加依赖:

def deps do
  [
    # 项目应该始终用 tag 版本,避免造成破坏
    {:ut_api_error, git: "git@github.com:choice-form/ut_api_error.git", tag: "v0.1.0"}
    # 本地开发以及连调推荐用 path
    # {:ut_api_error, git: "git@github.com:choice-form/ut_api_error.git"}
    # {:ut_api_error, path: "/path/to/ut_api_error"}
  ]
end

UtApiError 基础用法

具体看 UtApiError 模块文档。你基本只需要 buildtransform_details

通用 API 错误的处理

这些错误通常有:

  • 未验证 - 使用 code :unauthenticated
  • 未授权 - 使用 code :permission_denied
  • 资源找不到 - 使用 code :not_found
  • 请求参数校验错误 - 使用 code :invalid_argument

Phoenix 项目应该在 controller action 层面返回统一的 {:error, struct} 结构,然后在 FallbackController 中处理:

defmodule YourAppWeb.FallbackController do
  # 统一处理 UtApiError.Error
  def call(conn, {:error, %UtApiError.Error{} = error}) do
    render_error(conn, error)
  end

  # 统一处理 Ecto.Changeset
  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
    # 构建 UtApiError.Error 结构体,并把 changeset 转换成 details 结构体列表
    error =
      UtApiError.build(:invalid_argument,
        details: UtApiError.transform_details(changeset)
      )

    render_error(conn, error)
  end

  # 统一处理渲染,生成符合 API 规范的结构
  defp render_error(conn, error) do
    conn
    |> put_status(error.status)
    # Phoenix 1.7 使用 put_view(json: DemoApiErrorWeb.ErrorJSON)
    |> put_view(YourAppWeb.ErrorView)
    |> render(:api_error,
      request_id: get_request_id(conn),
      error: error
    )
  end

  # 获取 request_id ,为放入 response 做准备
  defp get_request_id(conn) do
    case get_resp_header(conn, "x-request-id") do
      [request_id] -> request_id
      _ -> nil
    end
  end
end

对应的 ErrorView

defmodule YourAppWeb.ErrorView do
  def render("api_error.json", %{request_id: request_id, error: api_error}) do
    # api_error 是 UtApiError.Error 结构体
    # 因为实现了 Jason.Encoder 协议,可以被自动转换成 JSON
    %{request_id: request_id, error: api_error}
  end
end

或对应的 ErrorJSON (in Phoenix 1.7) :

defmodule YourAppWeb.ErrorJSON do
  def api_error(%{request_id: request_id, error: api_error}) do
    # api_error 是 UtApiError.Error 结构体
    # 因为实现了 Jason.Encoder 协议,可以被自动转换成 JSON
    %{request_id: request_id, error: api_error}
  end
end

业务逻辑错误处理

这些错误一般是每个应用专有的,错误需求包括但不限于:

  • 定义业务错误代码
  • 定义额外的结构化数据,方便调用者处理

这需要在 controller action 里单独构建 UtApiError.Error 。大多数情况下它们应该使用 code :failed_precondition ,并把细节数据放到 details 中。

defmodule YourAppWeb.WorkflowController do
  def publish(conn, _) do
    with :ok <- Context.publish(conn) do
      render_ok()
    else
      {:error, {:publish_error, _reason}} ->
        # 把 context 中的错误转换成 UtApiError.Error 结构体
        # 业务错误的 code 和 details 需要自行决定
        api_error = UtApiError.build(
          :failed_precondition,
          # API 规范对 details 的定义是 array<object>
          details: [
            # detail 可以是 map 或结构体,结构体需要实现 Jason.Encoder 协议
            # ErrorInfo 是通用的带业务错误代码 (reason) 的数据结构
            %UtApiError.Details.ErrorInfo{
              reason: "business_code"
            }
          ]
        )

        # 返回统一结构,渲染仍然交给 FallbackController 处理
        {:error, api_error}
    end
  end
end

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages