- Published on
Express 使用中间件 responseHandler 格式化响应数据
- Authors
- Name
- Shelton Ma
常见约定
1. 统一响应结构
一个标准的 API 响应格式通常包含以下字段
{
"success": true, // 状态码,通常使用 true 表示成功,false 表示失败.
"message": "Success", // 请求的提示信息.
"data": {} // 请求的返回数据.
}
2. 在 TypeScript 中扩展 Response 类型
// src/typing.d.ts
import { ManagementClient, AuthenticationClient } from "authing-js-sdk";
import "express";
declare global {
var authing: {
managementClient: ManagementClient;
authenticationClient: AuthenticationClient;
};
namespace Express {
interface Response {
exportRows?: any[];
success: (params: { data?: any; message?: string }) => void;
fail: (params: {
httpStatus?: number;
success?: boolean;
message?: string;
data?: any;
}) => void;
}
}
}
3. 定义统一的 Response Middleware
// src/middlewares/response.middleware.ts
import { Request, Response, NextFunction } from "express";
interface SuccessResponse {
data?: any;
message?: string;
}
interface FailResponse {
success?: boolean;
message?: string;
data?: any;
httpStatus?: number;
}
// 统一响应格式封装
export function responseHandler(
req: Request,
res: Response,
next: NextFunction
) {
res.success = ({ data = null, message = "Success" }: SuccessResponse) => {
res.json({ success: true, message, data });
};
res.fail = ({
success = false,
message = "Error",
data = null,
httpStatus = 400,
}: FailResponse) => {
res.status(httpStatus).json({ success, message, data });
};
next();
}
4. 控制器调用
router.post(
"/",
validateRequest(z.object({ body: TicketCreateSchema })),
asyncHandler(createTicket)
);
5. 服务层调用
export const createTicket = async (
req: TypedRequest<TicketCreateData>,
res: Response
) => {
res.success({ data: result, message: "Ticket created successfully" });
};
根据业务逻辑引入业务状态吗
1. 业务逻辑需要更细粒度的错误区分
HTTP 状态码(如 200
, 400
, 404
)只能提供通用的状态,但无法区分具体的业务错误.例如:
场景 | HTTP 状态码 | **业务状态码 (code) | message 解释 |
---|---|---|---|
用户未登录 | 401 | 1001 | "未登录,请先登录" |
用户权限不足 | 403 | 1002 | "无权限访问该资源" |
账号已被冻结 | 403 | 1003 | "您的账号已被冻结,请联系客服" |
余额不足 | 400 | 1004 | "账户余额不足,无法下单" |
为什么不用 HTTP 状态码?
- 401(未授权)无法区分未登录和Token 过期
- 403(无权限)无法区分角色权限不足和账号被封禁
- 400(请求错误)无法区分余额不足和参数错误
2. 使用code 可扩展、可国际化、可读性强
// 国际化
const errorMessages = {
"1001": { en: "Not logged in", zh: "未登录" },
"1002": { en: "Permission denied", zh: "无权限访问" },
"1003": { en: "Account frozen", zh: "账号被冻结" }
};
showMessage(errorMessages[response.code][currentLang]);