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

API错误处理在Postman正常 PHP客户端抛出异常求助

问题描述

我的API错误处理逻辑在Postman中能返回清晰易懂的友好错误信息,但集成到PHP客户端(使用Guzzle库)时,总是抛出异常,且获取的响应不规整。

后端相关实现代码

工具类ErrorResponse

class ErrorResponse extends Error {
    constructor(message, statusCode) {
        super(message);
        this.statusCode = statusCode;

        Error.captureStackTrace(this, this.constructor);
    }
}

module.exports = ErrorResponse;

错误中间件errorHandler

const ErrorResponse = require('../Utils/errorResponse');

const errorHandler = (err, req, res, next) => {
    let error = {
        ...err
    };

    error.message = err.message;

    // Log to console for dev
    console.log(err);
    // console.log(err.response.data);

    // Mongoose bad ObjectId
    if (err.name === 'CastError') {
        const message = `Resource not found`;
        error = new ErrorResponse(message, 404);
    }

    // Mongoose duplicate key
    if (err.code === 11000) {
        const message = 'Duplicate field value entered';
        error = new ErrorResponse(message, 400);
    }

    // Mongoose validation error
    if (err.name === 'ValidationError') {
        const message = Object.values(err.errors).map(val => val.message);
        error = new ErrorResponse(message, 400);
    }

    res.status(error.statusCode || 500).json({
        "status": "error",
        "message": error.message || 'Server Error'
    });
};

module.exports = errorHandler;

错误调用示例

return next(new ErrorResponse('Invalid credentials', 401));

PHP端Guzzle代码

try {
    // 此处是Guzzle请求逻辑
} catch (GuzzleHttp\Exception\ClientException $e) {
   return $e->getResponse();
}
解决方案

问题核心在于Guzzle会对4xx/5xx状态码的响应自动抛出异常,但你的API确实返回了结构化的错误JSON,只是需要在异常中正确解析响应体,而不是直接返回整个响应对象。

步骤1:正确解析Guzzle异常中的响应体

修改PHP端的catch逻辑,获取并解析响应的JSON内容:

try {
    $client = new GuzzleHttp\Client();
    $response = $client->request('POST', 'your-api-endpoint', [
        // 请求参数配置(如headers、form_params等)
    ]);
    // 处理正常响应
    $data = json_decode($response->getBody(), true);
} catch (GuzzleHttp\Exception\ClientException $e) {
    // 获取4xx错误的状态码和响应体
    $statusCode = $e->getResponse()->getStatusCode();
    $errorBody = $e->getResponse()->getBody()->getContents();
    // 解析为关联数组
    $errorData = json_decode($errorBody, true);
    
    // 返回结构化的错误信息
    return [
        'status' => 'error',
        'code' => $statusCode,
        'message' => $errorData['message'] ?? 'Unknown error'
    ];
} catch (GuzzleHttp\Exception\ServerException $e) {
    // 处理5xx服务器错误
    $errorBody = $e->getResponse()->getBody()->getContents();
    $errorData = json_decode($errorBody, true);
    return [
        'status' => 'error',
        'code' => $e->getResponse()->getStatusCode(),
        'message' => $errorData['message'] ?? 'Server error'
    ];
}

步骤2:后端额外检查(可选)

确保错误中间件的有效性:

  • 确认errorHandler中间件在Express应用中注册在所有路由之后,否则无法捕获路由抛出的异常
  • 验证自定义ErrorResponse实例的属性是否被正确传递,比如在中间件中打印error对象确认statusCode和message是否存在
  • 可以在中间件末尾添加日志,输出返回的错误JSON,验证格式是否符合预期:
// 在errorHandler的res.status().json()前添加
const errorResponse = {
    "status": "error",
    "message": error.message || 'Server Error'
};
console.log('Error response sent:', JSON.stringify(errorResponse));
res.status(error.statusCode || 500).json(errorResponse);

关键说明

  • Guzzle将4xx状态码归为ClientException,5xx归为ServerException,分开捕获能更精准处理不同类型的错误
  • getBody()->getContents()会一次性读取响应体内容,若后续需要再次读取,需调用$e->getResponse()->getBody()->rewind()重置指针
  • 解析JSON时传入第二个参数true,将JSON对象转为PHP关联数组,方便直接取值

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 13:47:49