You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ember/Phoenix应用使用JSONAPIAdapter发起POST请求时AdapterError报错

为什么Ember会抛出AdapterError expects json-api formatted errors array错误?

这个问题我之前也碰到过,核心原因是Ember的JSONAPIAdapter严格遵循JSON:API规范,而你的Phoenix后端返回的错误响应结构不符合这个规范。

问题根源

JSON:API规范明确要求错误响应的errors字段必须是一个数组,每个数组元素是包含错误详情的对象(比如要包含detail、source等属性)。举个符合规范的错误响应示例:

{
  "errors": [
    {
      "detail": "can't be blank",
      "source": {
        "pointer": "/data/attributes/name"
      }
    }
  ]
}

但你的Phoenix后端返回的是:

{"errors":{"name":["can't be blank"]}}

这是Phoenix默认的Changeset错误格式——errors是一个对象,键是字段名,值是错误信息数组,完全不符合JSON:API的数组要求,所以Ember的适配器会抛出断言错误。


两种解决方案

方案1:修改Phoenix后端,返回符合JSON:API的错误格式

在Phoenix的控制器中,自定义错误处理逻辑,把Changeset的错误转换成JSON:API要求的数组结构。

首先在控制器里添加一个辅助函数,用于转换错误格式:

defp json_api_errors(changeset) do
  Enum.map(Ecto.Changeset.traverse_errors(changeset, &translate_error/1), fn {field, messages} ->
    %{
      detail: hd(messages),
      source: %{
        pointer: "/data/attributes/#{Atom.to_string(field)}"
      }
    }
  end)
end

然后在处理创建失败的逻辑中使用这个函数:

def create(conn, %{"data" => data}) do
  case Listings.create_listing(data) do
    {:ok, listing} ->
      render(conn, "show.json", listing: listing)
    {:error, %Ecto.Changeset{} = changeset} ->
      conn
      |> put_status(:unprocessable_entity)
      |> json(%{errors: json_api_errors(changeset)})
  end
end

这样后端返回的错误就完全符合Ember的要求了。

方案2:在Ember端适配后端的错误格式

如果暂时不想修改后端,可以在Ember的适配器中重写handleResponse方法,把后端的错误对象转换成JSON:API规范的数组格式。

修改adapter/application.js:

import DS from 'ember-data';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
import config from '../config/environment';

export default DS.JSONAPIAdapter.extend(DataAdapterMixin, {
  handleResponse(status, headers, payload) {
    // 检测到后端返回的错误是对象格式时,进行转换
    if (payload && payload.errors && typeof payload.errors === 'object' && !Array.isArray(payload.errors)) {
      const formattedErrors = Object.entries(payload.errors).map(([field, messages]) => {
        return {
          detail: messages[0],
          source: {
            pointer: `/data/attributes/${field}`
          }
        };
      });
      payload.errors = formattedErrors;
    }
    return this._super(status, headers, payload);
  }
});

这个方法会在适配器处理响应前,把后端的错误对象转换成Ember期望的数组结构,从而避免断言错误。

内容的提问来源于stack exchange,提问作者guy1997

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:52:18