错误处理

页面摘要: Strapi 的 API 以一致的结构返回错误,并允许后端代码为控制器、服务、策略或生命周期抛出自定义异常。本文档列出了错误类、上下文辅助函数,以及用于构建有意义响应的示例。

Strapi 原生以标准格式处理错误。

错误处理有 2 种使用场景:

  • 作为通过 REST 或 GraphQL API 查询内容的开发者,你可能会在响应中 收到错误。
  • 作为自定义 Strapi 应用后端的开发者,你可以使用控制器和服务来 抛出错误。

接收错误

错误包含在响应对象的 error 键中,并包括 HTTP 状态码、错误名称以及附加信息等信息。

REST 错误

REST API 抛出的错误包含在具有以下格式的 响应 中:

{
  "data": null,
  "error": {
    "status": "", // HTTP 状态码
    "name": "", // Strapi 错误名称('ApplicationError' 或 'ValidationError')
    "message": "", // 人类可读的错误消息
    "details": {
      // 特定于错误类型的错误详情
    }
  }
}

GraphQL 错误

GraphQL API 抛出的错误包含在具有以下格式的响应中:

{ "errors": [
    {
      "message": "", // 人类可读的错误消息
      "extensions": {
        "error": {
          "name": "", // Strapi 错误名称('ApplicationError' 或 'ValidationError'),
          "message": "", // 人类可读的错误消息(与上面相同);
          "details": {}, // 特定于错误类型的错误详情
        },
        "code": "" // GraphQL 错误代码(例如:BAD_USER_INPUT)
      }
    }
  ],
  "data": {
    "graphQLQueryName": null
  }
}

抛出错误

控制器与中间件

在使用 Strapi 开发任何自定义逻辑时,推荐抛出错误的方式是让 控制器 或 中间件 以正确的状态码和响应体进行响应。

这可以通过在上下文(即 ctx)上调用错误函数来完成。可用的错误函数在 http-errors 文档 中列出,但它们的名称应转换为小驼峰式才能被 Strapi 使用(例如 badRequest)。

错误函数接受 2 个参数,对应于查询 API 的开发者 接收 的 error.message 和 error.details 属性:

  • 函数的第一个参数是错误的 message(消息)
  • 第二个参数是将被设置为响应中 details 的对象

JavaScript

// path: ./src/api/[api-name]/controllers/my-controller.js

module.exports = {
  renameDog: async (ctx, next) => {
    const newName = ctx.request.body.name;
    if (!newName) {
      return ctx.badRequest('name is missing', { foo: 'bar' })
    }
    ctx.body = strapi.service('api::dog.dog').rename(newName);
  }
}

// path: ./src/api/[api-name]/middlewares/my-middleware.js

module.exports = async (ctx, next) => {
  const newName = ctx.request.body.name;
  if (!newName) {
    return ctx.badRequest('name is missing', { foo: 'bar' })
  }
  await next();
}

TypeScript

// path: ./src/api/[api-name]/controllers/my-controller.ts

export default {
  renameDog: async (ctx, next) => {
    const newName = ctx.request.body.name;
    if (!newName) {
      return ctx.badRequest('name is missing', { foo: 'bar' })
    }
    ctx.body = strapi.service('api::dog.dog').rename(newName);
  }
}

// path: ./src/api/[api-name]/middlewares/my-middleware.ts

export default async (ctx, next) => {
  const newName = ctx.request.body.name;
  if (!newName) {
    return ctx.badRequest('name is missing', { foo: 'bar' })
  }
  await next();
}

服务与模型生命周期

一旦你在比控制器或中间件更深的层级工作,就有专用的错误类可用于抛出错误。这些类是对 Node Error 类 的扩展,并专门针对某些使用场景。

这些错误类通过 @strapi/utils 包导入,可以从多个不同层级调用。以下示例使用服务层,但错误类并不限于服务和模型生命周期。在模型生命周期层抛出错误时,建议使用 ApplicationError 类,以便在管理面板中显示正确的错误消息。

NOTE

有关 Strapi 提供的错误类的更多信息,请参阅 默认错误类 章节。

示例:在服务中抛出错误**

此示例展示了包裹一个 核心服务 并对 create 方法执行自定义验证:

JavaScript


const { errors } = require('@strapi/utils');
const { ApplicationError } = errors;
const { createCoreService } = require('@strapi/strapi').factories;

module.exports = createCoreService('api::restaurant.restaurant', ({ strapi }) =>  ({
  async create(params) {
    let okay = false;

    // 抛出错误将阻止餐厅被创建
    if (!okay) {
      throw new errors.ApplicationError('Something went wrong', { foo: 'bar' });
    }
  
    const result = await super.create(params);

    return result;
  }
});

TypeScript


import { errors } from '@strapi/utils';
import { factories } from '@strapi/strapi';

const { ApplicationError } = errors;

export default factories.createCoreService('api::restaurant.restaurant', ({ strapi }) =>  ({
  async create(params) {
    let okay = false;

    // 抛出错误将阻止餐厅被创建
    if (!okay) {
      throw new errors.ApplicationError('Something went wrong', { foo: 'bar' });
    }
  
    const result = await super.create(params);

    return result;
  }
}));

示例:在模型生命周期中抛出错误**

此示例展示了构建一个 自定义模型生命周期,并能够抛出一个错误来停止请求,同时向管理面板返回正确的错误消息。通常你应该只在 beforeX 生命周期中抛出错误,而不是在 afterX 生命周期中。

JavaScript


const { errors } = require('@strapi/utils');
const { ApplicationError } = errors;

module.exports = {
  beforeCreate(event) {
    let okay = false;

    // 抛出错误将阻止实体被创建
    if (!okay) {
      throw new errors.ApplicationError('Something went wrong', { foo: 'bar' });
    }
  },
};

TypeScript


import { errors } from '@strapi/utils';
const { ApplicationError } = errors;

export default {
  beforeCreate(event) {
    let okay = false;

    // 抛出错误将阻止实体被创建
    if (!okay) {
      throw new errors.ApplicationError('Something went wrong', { foo: 'bar' });
    }
  },
};

策略

策略 是一种在控制器之前执行的特殊中间件。它们用于检查用户是否被允许执行该操作。如果用户不被允许执行该操作并且使用了 return false,则会抛出一个通用错误。作为替代方案,你可以使用从 Strapi ForbiddenError 类、ApplicationError 类(这两个类均参见 默认错误类)扩展的嵌套类,以及 Node Error 类,来抛出自定义错误消息。

PolicyError 类可从 @strapi/utils 包获取,接受 2 个参数:

  • 函数的第一个参数是错误的 message(消息)
  • (可选)第二个参数是将被设置为响应中 details 的对象;最佳实践是将 policy 键设置为抛出错误的策略名称。

示例:在自定义策略中抛出 PolicyError

此示例展示了构建一个 自定义策略,它将抛出自定义错误消息并停止请求。

JavaScript


const { errors } = require('@strapi/utils');
const { PolicyError } = errors;

module.exports = (policyContext, config, { strapi }) => {
  let isAllowed = false;

  if (isAllowed) {
    return true;
  } else {
    throw new errors.PolicyError('You are not allowed to perform this action', {
      policy: 'my-policy',
      myCustomKey: 'myCustomValue',
    });
  }
}

TypeScript


import { errors } from '@strapi/utils';
const { PolicyError } = errors;

export default (policyContext, config, { strapi }) => {
  let isAllowed = false;

  if (isAllowed) {
    return true;
  } else {
    throw new errors.PolicyError('You are not allowed to perform this action', {
      policy: 'my-policy',
      myCustomKey: 'myCustomValue',
    });
  }
};

默认错误类

默认错误类可从 @strapi/utils 包获取,并可在你的代码中导入和使用。任何默认错误类都可以被扩展以创建自定义错误类。然后该自定义错误类可用于在代码中抛出错误。

Application

ApplicationError 类是用于应用程序错误的通用错误类,通常推荐作为默认错误类。这个类专门设计用于抛出管理面板能够读取并展示给用户的适当错误消息。它接受以下参数:

参数类型描述默认值
messagestring错误消息An application error occurred
detailsobject用于定义附加详细信息的对象{}
throw new errors.ApplicationError('Something went wrong', { foo: 'bar' });

Pagination

PaginationError 类是一个特定错误类,通常在解析来自 REST、GraphQL 或 文档服务 的分页信息时使用。它接受以下参数:

参数类型描述默认值
messagestring错误消息Invalid pagination
throw new errors.PaginationError('Exceeded maximum pageSize limit');

NotFound

NotFoundError 类是用于抛出 404 状态码错误的通用错误类。它接受以下参数:

参数类型描述默认值
messagestring错误消息Entity not found
throw new errors.NotFoundError('These are not the droids you are looking for');

Forbidden

ForbiddenError 类是一个特定错误类,用于在用户未提供任何或正确的身份验证凭据时使用。它接受以下参数:

参数类型描述默认值
messagestring错误消息Forbidden access
throw new errors.ForbiddenError('Ah ah ah, you didn\'t say the magic word');
NOTE

传递给 ForbiddenError 的自定义 message 不会包含在 API 响应中。相反,API 返回默认消息。如果你需要返回指示权限不足的自定义错误消息,请改用 PolicyError。

Unauthorized

UnauthorizedError 类是一个特定错误类,用于在用户已正确通过身份验证、但没有执行特定操作的正确角色或权限时使用。它接受以下参数:

参数类型描述默认值
messagestring错误消息Unauthorized
throw new errors.UnauthorizedError('You shall not pass!');

NotImplemented

NotImplementedError 类是一个特定错误类,用于传入的请求尝试使用当前未实现或未配置的功能时。它接受以下参数:

参数类型描述默认值
messagestring错误消息This feature is not implemented yet
throw new errors.NotImplementedError('This isn\'t implemented', { feature: 'test', implemented: false });

PayloadTooLarge

PayloadTooLargeError 类是一个特定错误类,用于传入的请求体或附加文件超出服务器限制时。它接受以下参数:

参数类型描述默认值
messagestring错误消息Entity too large
throw new errors.PayloadTooLargeError('Uh oh, the file too big!');

Policy

PolicyError 类是一个特定错误,设计用于与 路由策略 一起使用。最佳实践建议确保策略名称在 details 参数中传递。它接受以下参数:

参数类型描述默认值
messagestring错误消息Policy Failed
detailsobject用于定义附加详细信息的对象{}
throw new errors.PolicyError('Something went wrong', { policy: 'my-policy' });
TIP

要在管理面板中处理 HTTP 错误(例如,在发起 API 请求的插件中),请使用 Strapi 内置 fetch 客户端的 isFetchError 工具和 FetchError 类。详情请参阅 管理面板 API:Fetch 客户端 > 错误处理。