当前位置: 首页 > 知识库问答 >
问题:

javascript - 使用 async await 调用接口时,报错,Error: Unsuccessful HTTP response?

洪子晋
2026-01-08

使用 async await 调用接口时,报错,Error: Unsuccessful HTTP response,是哪块的错误?

调用接口代码

          await aUserApi.apiSysUserUpdateInfoPost(
            { body: this.EditAccountForm },
            (error, data, response) => {
              // ...
            }
          );
        }

ApiClient.js

/*
 * 通用权限管理平台
 * 前后端分离架构,开箱即用,紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
 *
 * OpenAPI spec version: 1.0.0
 *
 * NOTE: This class is auto generated by the swagger code generator program.
 * https://github.com/swagger-api/swagger-codegen.git
 *
 * Swagger Codegen version: 3.0.66
 *
 * Do not edit the class manually.
 *
 */
import superagent from "superagent";
import {
  removeToken
} from '@/utils/auth'
import Cookies from 'js-cookie'

/**
 * @module ApiClient
 * @version 1.0.0
 */

/**
 * Manages low level client-server communications, parameter marshalling, etc. There should not be any need for an
 * application to use this class directly - the *Api and model classes provide the public API for the service. The
 * contents of this file should be regarded as internal but are documented for completeness.
 * @alias module:ApiClient
 * @class
 */
export default class ApiClient {
  constructor() {
    /**
     * The base URL against which to resolve every API call's (relative) path.
     * @type {String}
     * @default /
     */
    this.basePath = process.env.VUE_APP_BASE_API.replace(/\/+$/, '');

    /**
     * The authentication methods to be included for all API calls.
     * @type {Array.<String>}
     */
    this.authentications = {
      // 使用者配置
      'Bearer': {
        type: 'basic'
      }
    }

    /**
     * The default HTTP headers to be included for all API calls.
     * @type {Array.<String>}
     * @default {}
     */
    this.defaultHeaders = {
      'Authorization': null,
      'X-Authorization': null
    };

    /**
     * The default HTTP timeout for all API calls.
     * @type {Number}
     * @default 60000
     */
    this.timeout = 600000;

    /**
     * If set to false an additional timestamp parameter is added to all API GET calls to
     * prevent browser caching
     * @type {Boolean}
     * @default true
     */
    this.cache = true;

    /**
     * If set to true, the client will save the cookies from each server
     * response, and return them in the next request.
     * @default false
     */
    this.enableCookies = false;

    /*
     * Used to save and return cookies in a node.js (non-browser) setting,
     * if this.enableCookies is set to true.
     */
    if (typeof window === 'undefined') {
      this.agent = new superagent.agent();
    }

    /*
     * Allow user to override superagent agent
     */
    this.requestAgent = null;

  }

  /**
   * Returns a string representation for an actual parameter.
   * @param param The actual parameter.
   * @returns {String} The string representation of <code>param</code>.
   */
  paramToString(param) {
    if (param == undefined || param == null) {
      return '';
    }
    if (param instanceof Date) {
      return param.toJSON();
    }

    return param.toString();
  }

  /**
   * Builds full URL by appending the given path to the base URL and replacing path parameter place-holders with parameter values.
   * NOTE: query parameters are not handled here.
   * @param {String} path The path to append to the base URL.
   * @param {Object} pathParams The parameter values to append.
   * @returns {String} The encoded path with parameter values substituted.
   */
  buildUrl(path, pathParams) {
    if (!path.match(/^\//)) {
      path = '/' + path;
    }

    var url = this.basePath + path;
    url = url.replace(/\{([\w-]+)\}/g, (fullMatch, key) => {
      var value;
      if (pathParams.hasOwnProperty(key)) {
        value = this.paramToString(pathParams[key]);
      } else {
        value = fullMatch;
      }

      return encodeURIComponent(value);
    });

    return url;
  }

  /**
   * Checks whether the given content type represents JSON.<br>
   * JSON content type examples:<br>
   * <ul>
   * <li>application/json</li>
   * <li>application/json; charset=UTF8</li>
   * <li>APPLICATION/JSON</li>
   * </ul>
   * @param {String} contentType The MIME content type to check.
   * @returns {Boolean} <code>true</code> if <code>contentType</code> represents JSON, otherwise <code>false</code>.
   */
  isJsonMime(contentType) {
    return Boolean(contentType != null && contentType.match(/^application\/json(;.*)?$/i));
  }

  /**
   * Chooses a content type from the given array, with JSON preferred; i.e. return JSON if included, otherwise return the first.
   * @param {Array.<String>} contentTypes
   * @returns {String} The chosen content type, preferring JSON.
   */
  jsonPreferredMime(contentTypes) {
    for (var i = 0; i < contentTypes.length; i++) {
      if (this.isJsonMime(contentTypes[i])) {
        return contentTypes[i];
      }
    }

    return contentTypes[0];
  }

  /**
   * Checks whether the given parameter value represents file-like content.
   * @param param The parameter to check.
   * @returns {Boolean} <code>true</code> if <code>param</code> represents a file.
   */
  isFileParam(param) {
    // fs.ReadStream in Node.js and Electron (but not in runtime like browserify)
    if (typeof require === 'function') {
      let fs;
      try {
        fs = require('fs');
      } catch (err) {}
      if (fs && fs.ReadStream && param instanceof fs.ReadStream) {
        return true;
      }
    }

    // Buffer in Node.js
    if (typeof Buffer === 'function' && param instanceof Buffer) {
      return true;
    }

    // Blob in browser
    if (typeof Blob === 'function' && param instanceof Blob) {
      return true;
    }

    // File in browser (it seems File object is also instance of Blob, but keep this for safe)
    if (typeof File === 'function' && param instanceof File) {
      return true;
    }

    return false;
  }

  /**
   * Normalizes parameter values:
   * <ul>
   * <li>remove nils</li>
   * <li>keep files and arrays</li>
   * <li>format to string with `paramToString` for other cases</li>
   * </ul>
   * @param {Object.<String, Object>} params The parameters as object properties.
   * @returns {Object.<String, Object>} normalized parameters.
   */
  normalizeParams(params) {
    var newParams = {};
    for (var key in params) {
      if (params.hasOwnProperty(key) && params[key] != undefined && params[key] != null) {
        var value = params[key];
        if (this.isFileParam(value) || Array.isArray(value)) {
          newParams[key] = value;
        } else {
          newParams[key] = this.paramToString(value);
        }
      }
    }

    return newParams;
  }

  /**
   * Enumeration of collection format separator strategies.
   * @enum {String}
   * @readonly
   */
  static CollectionFormatEnum = {
    /**
     * Comma-separated values. Value: <code>csv</code>
     * @const
     */
    CSV: ',',

    /**
     * Space-separated values. Value: <code>ssv</code>
     * @const
     */
    SSV: ' ',

    /**
     * Tab-separated values. Value: <code>tsv</code>
     * @const
     */
    TSV: '\t',

    /**
     * Pipe(|)-separated values. Value: <code>pipes</code>
     * @const
     */
    PIPES: '|',

    /**
     * Native array. Value: <code>multi</code>
     * @const
     */
    MULTI: 'multi'
  };

  /**
   * Builds a string representation of an array-type actual parameter, according to the given collection format.
   * @param {Array} param An array parameter.
   * @param {module:ApiClient.CollectionFormatEnum} collectionFormat The array element separator strategy.
   * @returns {String|Array} A string representation of the supplied collection, using the specified delimiter. Returns
   * <code>param</code> as is if <code>collectionFormat</code> is <code>multi</code>.
   */
  buildCollectionParam(param, collectionFormat) {
    if (param == null) {
      return null;
    }
    switch (collectionFormat) {
      case 'csv':
        return param.map(this.paramToString).join(',');
      case 'ssv':
        return param.map(this.paramToString).join(' ');
      case 'tsv':
        return param.map(this.paramToString).join('\t');
      case 'pipes':
        return param.map(this.paramToString).join('|');
      case 'multi':
        //return the array directly as SuperAgent will handle it as expected
        return param.map(this.paramToString);
      default:
        throw new Error('Unknown collection format: ' + collectionFormat);
    }
  }

  /**
   * Applies authentication headers to the request.
   * @param {Object} request The request object created by a <code>superagent()</code> call.
   * @param {Array.<String>} authNames An array of authentication method names.
   */
  applyAuthToRequest(request, authNames) {
    authNames.forEach((authName) => {
      var auth = this.authentications[authName];
      switch (auth.type) {
        case 'basic':
          if (auth.username || auth.password) {
            request.auth(auth.username || '', auth.password || '');
          }

          break;
        case 'apiKey':
          if (auth.apiKey) {
            var data = {};
            if (auth.apiKeyPrefix) {
              data[auth.name] = auth.apiKeyPrefix + ' ' + auth.apiKey;
            } else {
              data[auth.name] = auth.apiKey;
            }

            if (auth['in'] === 'header') {
              request.set(data);
            } else {
              request.query(data);
            }
          }

          break;
        case 'oauth2':
          if (auth.accessToken) {
            request.set({
              'Authorization': 'Bearer ' + auth.accessToken
            });
          }

          break;
        default:
          throw new Error('Unknown authentication type: ' + auth.type);
      }
    });
  }

  /**
   * Deserializes an HTTP response body into a value of the specified type.
   * @param {Object} response A SuperAgent response object.
   * @param {(String|Array.<String>|Object.<String, Object>|Function)} returnType The type to return. Pass a string for simple types
   * or the constructor function for a complex type. Pass an array containing the type name to return an array of that type. To
   * return an object, pass an object with one property whose name is the key type and whose value is the corresponding value type:
   * all properties on <code>data<code> will be converted to this type.
   * @returns A value of the specified type.
   */
  deserialize(response, returnType) {
    if (response == null || returnType == null || response.status == 204) {
      return null;
    }

    // Rely on SuperAgent for parsing response body.
    // See http://visionmedia.github.io/superagent/#parsing-response-bodies
    var data = response.body;
    if (data == null || (typeof data === 'object' && typeof data.length === 'undefined' && !Object.keys(data).length)) {
      // SuperAgent does not always produce a body; use the unparsed response as a fallback
      data = response.text;
    }

    return ApiClient.convertToType(data, returnType);
  }

  /**
   * Callback function to receive the result of the operation.
   * @callback module:ApiClient~callApiCallback
   * @param {String} error Error message, if any.
   * @param data The data returned by the service call.
   * @param {String} response The complete HTTP response.
   */

  /**
   * Invokes the REST service using the supplied settings and parameters.
   * @param {String} path The base URL to invoke.
   * @param {String} httpMethod The HTTP method to use.
   * @param {Object.<String, String>} pathParams A map of path parameters and their values.
   * @param {Object.<String, Object>} queryParams A map of query parameters and their values.
   * @param {Object.<String, Object>} headerParams A map of header parameters and their values.
   * @param {Object.<String, Object>} formParams A map of form parameters and their values.
   * @param {Object} bodyParam The value to pass as the request body.
   * @param {Array.<String>} authNames An array of authentication type names.
   * @param {Array.<String>} contentTypes An array of request MIME types.
   * @param {Array.<String>} accepts An array of acceptable response MIME types.
   * @param {(String|Array|ObjectFunction)} returnType The required type to return; can be a string for simple types or the
   * constructor for a complex type.
   * @param {module:ApiClient~callApiCallback} callback The callback function.
   * @returns {Object} The SuperAgent request object.
   */
  callApi(path, httpMethod, pathParams,
    queryParams, headerParams, formParams, bodyParam, authNames, contentTypes, accepts,
    returnType, callback) {

    var url = this.buildUrl(path, pathParams);
    var request = superagent(httpMethod, url);

    // 申请验证
    this.applyAuthToRequest(request, authNames);

    // set query parameters
    if (httpMethod.toUpperCase() === 'GET' && this.cache === false) {
      queryParams['_'] = new Date().getTime();
    }

    request.query(this.normalizeParams(queryParams));

    // 设置头部参数
    request.set(this.defaultHeaders).set(this.normalizeParams(headerParams));

    // set requestAgent if it is set by user
    if (this.requestAgent) {
      request.agent(this.requestAgent);
    }

    // set request timeout
    request.timeout(this.timeout);

    var contentType = this.jsonPreferredMime(contentTypes);
    if (contentType) {
      // Issue with superagent and multipart/form-data (https://github.com/visionmedia/superagent/issues/746)
      if (contentType != 'multipart/form-data') {
        request.type(contentType);
      }
    } else if (!request.header['Content-Type']) {
      request.type('application/json');
    }

    if (contentType === 'application/x-www-form-urlencoded') {
      request.send(new URLSearchParams(this.normalizeParams(formParams)));
    } else if (contentType == 'multipart/form-data') {
      var _formParams = this.normalizeParams(formParams);
      for (var key in _formParams) {
        if (_formParams.hasOwnProperty(key)) {
          if (this.isFileParam(_formParams[key])) {
            // file field
            request.attach(key, _formParams[key]);
          } else {
            request.field(key, _formParams[key]);
          }
        }
      }
    } else if (bodyParam) {
      request.send(bodyParam);
    }

    var accept = this.jsonPreferredMime(accepts);
    if (accept) {
      request.accept(accept);
    }

    if (returnType === 'Blob') {
      request.responseType('blob');
    } else if (returnType === 'String') {
      request.responseType('string');
    }

    // Attach previously saved cookies, if enabled
    if (this.enableCookies) {
      if (typeof window === 'undefined') {
        this.agent.attachCookies(request);
      } else {
        request.withCredentials();
      }
    }

    request.end((error, response) => {
      if (callback) {
        var data = null;
        if (!error) {
          try {
            // data = this.deserialize(response, returnType);
            data = response;
            if (this.enableCookies && typeof window === 'undefined') {
              this.agent.saveCookies(response);
            }

            // 401 token 超时,跳转到登录页面
            if (data.body.Code == 401) {
              removeToken()
              location.reload();
            }
          } catch (err) {
            error = err;
          }
        }

        callback(error, data, response);
      }
    });

    return request;
  }

  /**
   * Parses an ISO-8601 string representation of a date value.
   * @param {String} str The date value as a string.
   * @returns {Date} The parsed date object.
   */
  static parseDate(str) {
    return new Date(str);
  }

  /**
   * Converts a value to the specified type.
   * @param {(String|Object)} data The data to convert, as a string or object.
   * @param {(String|Array.<String>|Object.<String, Object>|Function)} type The type to return. Pass a string for simple types
   * or the constructor function for a complex type. Pass an array containing the type name to return an array of that type. To
   * return an object, pass an object with one property whose name is the key type and whose value is the corresponding value type:
   * all properties on <code>data<code> will be converted to this type.
   * @returns An instance of the specified type or null or undefined if data is null or undefined.
   */
  static convertToType(data, type) {
    if (data === null || data === undefined)
      return data

    switch (type) {
      case 'Boolean':
        return Boolean(data);
      case 'Integer':
        return parseInt(data, 10);
      case 'Number':
        return parseFloat(data);
      case 'String':
        return String(data);
      case 'Date':
        return ApiClient.parseDate(String(data));
      case 'Blob':
        return data;
      default:
        if (type === Object) {
          // generic object, return directly
          return data;
        } else if (typeof type === 'function') {
          // for model type like: User
          return type.constructFromObject(data);
        } else if (Array.isArray(type)) {
          // for array type like: ['String']
          var itemType = type[0];

          return data.map((item) => {
            return ApiClient.convertToType(item, itemType);
          });
        } else if (typeof type === 'object') {
          // for plain object type like: {'String': 'Integer'}
          var keyType, valueType;
          for (var k in type) {
            if (type.hasOwnProperty(k)) {
              keyType = k;
              valueType = type[k];
              break;
            }
          }

          var result = {};
          for (var k in data) {
            if (data.hasOwnProperty(k)) {
              var key = ApiClient.convertToType(k, keyType);
              var value = ApiClient.convertToType(data[k], valueType);
              result[key] = value;
            }
          }

          return result;
        } else {
          // for unknown type, return the data directly
          return data;
        }
    }
  }

  /**
   * Constructs a new map or array model from REST data.
   * @param data {Object|Array} The REST data.
   * @param obj {Object|Array} The target object or array.
   */
  static constructFromObject(data, obj, itemType) {
    if (Array.isArray(data)) {
      for (var i = 0; i < data.length; i++) {
        if (data.hasOwnProperty(i))
          obj[i] = ApiClient.convertToType(data[i], itemType);
      }
    } else {
      for (var k in data) {
        if (data.hasOwnProperty(k))
          obj[k] = ApiClient.convertToType(data[k], itemType);
      }
    }
  };
}

/**
 * The default API client implementation.
 * @type {module:ApiClient}
 */
ApiClient.instance = new ApiClient();

共有1个答案

颛孙信厚
2026-01-08

基于提供的代码和分析,出现 Error: Unsuccessful HTTP response 的原因和解决方案如下:

错误原因

  1. HTTP 状态码非 2xx
    该错误表明接口返回了非成功的 HTTP 状态码(如 401、403、404、500 等)。superagent 默认将非 2xx 响应视为错误。
  2. 回调函数与 async/await 冲突
    您的调用方式混合了 async/await 和回调函数:

    await aUserApi.apiSysUserUpdateInfoPost(
      { body: this.EditAccountForm },
      (error, data, response) => { /* ... */ } // ❌ 回调函数与 await 冲突
    );

    superagent.end() 方法在同时使用 await 和回调函数时行为异常,导致错误处理失效。

  3. 401 Token 过期处理
    ApiClient.js 中处理了 401 状态码(自动跳转登录),但其他非 2xx 错误(如 400、500)未被捕获,会直接抛出 Unsuccessful HTTP response

解决方案

1. 移除回调函数,改用 try/catch 捕获异常

try {
  const response = await aUserApi.apiSysUserUpdateInfoPost({
    body: this.EditAccountForm
  });
  // 处理成功响应
  console.log("请求成功:", response.body);
} catch (error) {
  // 捕获 HTTP 错误
  console.error("请求失败:", error.response?.status, error.message);
  
  // 额外处理特定错误(可选)
  if (error.response?.status === 401) {
    console.error("Token 过期,请重新登录");
  }
}

2. 修改 ApiClient 以支持 Promise(推荐)

ApiClient.js 中重写 callApi 方法,使其返回 Promise 并正确 reject 错误:

callApi(...args) {
  return new Promise((resolve, reject) => {
    superagent(...)
      .end((error, response) => {
        if (error || !response.ok) {
          // 显式拒绝非 2xx 响应
          reject(error || new Error(`HTTP ${response.status}`));
        } else {
          resolve(response);
        }
      });
  });
}

3. 检查具体错误原因

在开发者工具(F12)的 Network 标签中:

  1. 定位失败的请求。
  2. 查看 Status Code(如 400、500)。
  3. 检查 Response Body 中的服务端错误详情(如参数错误、权限问题)。

关键修改说明

  1. 避免混用 await 和回调
    async/await 与回调是两种不同异步模式,混用会导致 Promise 链断裂。
  2. 正确处理 HTTP 错误
    非 2xx 响应需通过 try/catch.catch() 捕获,不可依赖回调函数。
  3. 验证请求参数
    确保 this.EditAccountForm 格式符合接口要求(可通过控制台打印或断点调试)。
提示:如果问题持续,请检查服务端日志或使用 Postman 测试接口,排除服务端问题。
 类似资料:
  • 大佬们帮忙看一个问题,关于Vue3 SSR onServerPrefetch生命周期的 我是想在该生命周期里调用接口获取数据然后直接在HTML里渲染,结果报错了 这是报错信息!!! 我查看了Vue文档SSR的章节https://cn.vuejs.org/guide/scaling-up/ssr.html#component-life...,并没有找到问题所在,甚至连示例都一致。

  • 4.2 接口调用 Camel管理端定义了两个版本的接口。第一版接口路径以"/api/"开头,第二版接口路径以"/api/v2"开头。 两个版本接口的主要区别在于:第二版本接口将更改配置、发布配置文件这两部操作聚合成为一个原子操作。则调用第二版本接口,如果成功,则Nginx当前配置为更改之后的配置;如果失败,则Nginx当前配置为调用接口之前的配置。不会出现不安全的中间状态。 第一版本接口: 更新节

  • 一般在开始使用tendermint之前, 作为开发者应该最关心的就是abci接口了, 因为这个是和tendermint进行交互的关键。 个人觉得这个也是tendermint的优势之一。 有了这个接口定义才有了实现通用区块链平台的可能。 所以说如果作为一个开发者, 不想了解整个tendermint的流转流程, 只想实现自己特定功能的区块链, 那么这一篇文章至少是应该看得。 我会从客户端创建开始说起,

  • 问题内容: 如何使用链接调用JavaScript代码? 问题答案: 要么 编辑: 上面的回答确实不是一个好的解决方案,自从我最初发布以来,已经学到了很多有关JS的知识.

  • 问题内容: 当用户结束调整浏览器窗口大小时,jQuery或JavaScript有什么办法触发函数? 换句话说: 用户调整浏览器窗口大小时是否可以检测到鼠标向上移动事件?除此以外: 我可以检测到窗口调整大小操作何时完成吗? 我目前只能在用户开始使用jQuery调整窗口大小时触发事件 问题答案: 您可以使用每次实际改变宽度/高度时获取,如下所示: 它会使用新的高度/宽度值并在页面中对其进行更新以供您查

  • 本文向大家介绍使用 MyBatis 的 mapper 接口调用时有哪些要求?相关面试题,主要包含被问及使用 MyBatis 的 mapper 接口调用时有哪些要求?时的应答技巧和注意事项,需要的朋友参考一下 Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同; Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 paramet

  • 问题内容: 此处有一篇文章:https : //gist.github.com/JonathanRaiman/f2ce5331750da7b2d4e9,通过仅调用Fortran库(BLAS / LAPACK / Intel MKL / OpenBLAS /随NumPy一起安装的任何库),都显示了极大的速度改进。经过数小时的研究(由于不建议使用的SciPy库),我终于得到了编译,但没有结果。它比Nu

  • 主要内容:1.Aware是什么时候赋值进来的,2.Aware接口的作用,3.Aware接口如何调用,4.测试,5.执行流程Aware接口是利用的回调机制, 将资源注入ioc容器 1.Aware是什么时候赋值进来的 1.createBeanInstance实例化 bean 2.populateBean属性注入 3.initializingBean初始化对象 createBeanInstance:getBean -> doGetBean -> createBean -> doCreateBean i