npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

gl-ajax

v1.0.2

Published

ajax

Downloads

8

Readme

Ajax 客户端库

支持统一管理服务器和接口地址、数据格式的 Ajax 客户端库。

安装

npm install gl-ajax

用法

选择Ajax请求库

  • 此库并不直接处理底层 Ajax 请求,而是在其他 Ajax 请求库之上进行封装,支持与以下常用 Ajax 请求库结合使用:

    • axios
    • jQuery.ajax(尚未实现)
    • fetch(尚未实现)
  • 针对每一种请求库,都提供了两种模块格式,用于不同的引用方式

    • esm 格式:使用 ES6 的 import 语句导入
    • umd 格式:使用 CommonJS 或 AMD 的 require 语句导入,或通过 script 标签引用(全局对象)
  • ES6 模块引用示例:

import Ajax from '@yondervision/ajax/dist/ajax-jquery.es';
//或
import Ajax from '@yondervision/ajax/dist/ajax-axios.es';
//或
import Ajax from '@yondervision/ajax/dist/ajax-fetch.es';
  • CommonJS 模块引用示例:
const Ajax = require('@yondervision/ajax/dist/ajax-jquery');
//或
const Ajax = require('@yondervision/ajax/dist/ajax-axios');
//或
const Ajax = require('@yondervision/ajax/dist/ajax-fetch');
  • script 标签引用示例(非模块化开发):
<!-- 需要将引用的 js 文件放在 http 可访问的目录下,输出全局对象 Ajax -->
<script src="/lib/ajax-jquery.js"></script>
<!-- 或 -->
<script src="/lib/ajax-axios.js"></script>
<!-- 或 -->
<script src="/lib/ajax-fetch.js"></script>

浏览器兼容性

  • 支持 Chrome 和 Firefox 的最新版本
  • 支持 IE 10 和 IE 11,在 IE 中使用需要先引入 polyfill.min.js(包括 Promise、URL、String.prototype.includes、regenerator-runtime)

初始化

var ajax = new Ajax({
  server: {}, //服务器定义
  api: {},    //接口定义
}, lib);      //底层 Ajax 请求库对象(通过 script 标签引用时可省略)

服务器定义

  • 如果只需要访问唯一的一个服务器,可直接在 server 选项中设置服务器参数
var ajax = new Ajax({
  server: {
    //基础URL,所有接口URL都相对于此地址,缺省为"/"
    baseUrl: 'http://10.22.11.10/someApi/',
    //默认请求方法,缺省为"POST"
    method: 'GET',
    //默认请求超时时间,单位毫秒,如果请求超过该时间后未得到响应,则会自动取消,缺省为0(代表不设置超时)
    timeout: 0,
    //服务器响应数据格式
    format: {
      //返回码字段名,如果设为false(布尔值),将只使用HTTP响应码判断成功
      codeKey: 'returnCode',
      //返回信息字段名
      msgKey: 'message',
      //返回数据字段名,如果直接在首层返回数据可设为null
      dataKey: 'returnData',
      //代表成功的返回码
      succCode: 0,
    },
    //返回给调用代码的响应数据格式(建议设置 msgKey 为 'message',以便统一处理各种异常)
    resFormat: {
      //格式同上
    },
  },
  api: {},
});
  • 如果需要访问多个服务器,可以在 server 选项中分别设置每个服务器的参数,key 为任意设置的服务器 ID,在接口参数配置中使用
var ajax = new Ajax({
  server: {
    server1: {
      baseUrl: 'http://10.22.11.10/someApi/',
      method: 'GET',
      format: {
        codeKey: 'returnCode',
        msgKey: 'message',
        dataKey: 'returnData',
        succCode: 0,
      },
    },
    server2: {
      baseUrl: 'http://10.22.11.20/otherApi/',
      method: 'POST',
      format: {
        codeKey: 'code',
        msgKey: 'msg',
        dataKey: 'data',
        succCode: '000000',
      },
    }
  },
  api: {},
});
  • 在创建了 Ajax 对象之后,还可以通过 setServer(config, serverId) 方法动态添加或修改服务器配置信息。该方法一次只能添加或修改一个服务器的信息,如果一共只有一个未指定 ID 的服务器,可省略 serverId 参数。
var ajax = new Ajax({
  server: {
    server1: { baseUrl: 'http://10.22.11.1:9080/api/', method: 'POST' },
    server2: { baseUrl: 'http://10.22.11.1:9081/api/', format: { msgKey: 'msg', succCode: '000' }},
  }
});
//修改现有服务器,指定的参数会被新的值覆盖,未指定的参数会继承原有的值
ajax.setServer({ baseUrl: 'http://11.22.33.44/new/', format: { succCode: '999' }}, 'server2');
//增加新的服务器
ajax.setServer({ baseUrl: 'http://10.22.11.1:9083/', method: 'GET' }, 'server3');

/* 以上执行完毕后,最终服务器配置信息如下:
{
  server1: { baseUrl: 'http://10.22.11.1:9080/api/', method: 'POST' },
  server2: { baseUrl: 'http://11.22.33.44/new/', format: { msgKey: 'msg', succCode: '999' }},
  server3: { baseUrl: 'http://10.22.11.1:9083/', method: 'GET' },
}
*/

接口定义

  • 注意:为了方便易用,在发送请求时只需要提供接口 ID,无需指定服务器。所以,如果定义了多个服务器,需要保证每个接口 ID 在所有服务器中都是唯一的,否则后定义的接口会覆盖先定义的接口。
  • 如果只有一个服务器,可以直接在 api 选项中指定接口配置
var ajax = new Ajax({
  server: {
    //省略……
  },
  api: {
    //查询所有用户列表
    listUser: 'GET user/list',
    //添加用户
    addUser: 'POST user',
    //删除用户
    delUser: 'DELETE user/:id',
    //修改用户信息
    editUser: 'PUT user/:id',
    //……
  },
});
  • 如果定义了多个服务器,可通过 setApi() 方法分别给每个服务器添加接口定义,第二个参数是服务器 ID
var ajax = new Ajax({
  server: {
    //组织机构服务器配置
    orgServer: {/* 省略…… */},
    //产品信息服务器配置
    prdServer: {/* 省略…… */},
  }
});

//组织机构服务接口配置
ajax.setApi({
  listUser: 'GET user/list',
  addUser: 'POST user',
  delUser: 'DELETE user/:id',
  editUser: 'PUT user/:id',
  //……
}, 'orgServer');

//产品信息服务接口配置
ajax.setApi({
  listProduct: 'GET product/list',
  addProduct: 'POST product',
  delProduct: 'DELETE product/:id',
  editProduct: 'PUT product/:id',
  //……
}, 'prdServer');
  • 只有一个服务器的时候也可以使用 setApi() 定义接口,此时不需要传第二个参数

接口定义格式

  • 可以使用字符串 "请求方法 接口地址" 来定义,例如:
  listUser: 'GET user/list',
  • 可以省略 请求方法 部分,将使用服务器定义中 method 选项指定的默认请求方法,例如:
  listUser: 'user/list',
  • 也可以使用对象方式来定义接口,url 是必需的,method 可以省略, 例如:
  listUser: {
    method: 'GET',
    url: 'user/list',
  },
  • 使用对象方式定义接口的好处是可以添加静态上传字段,其典型应用场景是,后台接口要求必须固定提交某些字段,但这些字段与业务逻辑无关,写在业务代码中的请求上传参数里不合适,就可以定义在接口的 key 参数里,它们会在每次请求该接口时自动提交,这样业务代码里就无需考虑这些字段了,例如:
  listUser: {
    method: 'GET',
    url: 'user/list',
    key: {
      channel: 'web',
      transCode: '142857',
    }
  }
  • 接口 key 参数的另一个典型应用场景是,多个不同功能的后台接口,可能使用同一个接口地址,只是通过某个标志字段来区分。如果只定义成一个接口,违背了接口设计的单一职责原则,如果将来后台改成了多个接口,还不得不修改业务代码。这种情况下,就可以将每个功能分别定义成不同的接口,通过 key 参数来区分功能,业务代码无需考虑传哪个标志字段,将来如果后台改成了多个接口,也无需修改业务代码,例如:
  listUser: {
    method: 'GET',
    url: 'info/list',
    key: {
      infoType: 'user',
    }
  },
  listProduct: {
    method: 'GET',
    url: 'info/list',
    key: {
      infoType: 'product',
    }
  },
  • 接口 url 中可以包含一个或多个冒号开头的变量,在发送请求时,变量将被上传数据中的同名值替换
{
  listUser: 'GET user/:userId',
}
  • baseUrl 一般包含服务器地址,例如 baseUrl: 'http://10.22.11.10/someApi/',如果前端页面与后端接口部署于同一个应用,同域调用,也可以省略服务器部分,例如 baseUrl: '/someApi/'。如果未提供 baseUrl 参数,其缺省值为 baseUrl: '/'

  • 如果 url 写成相对路径(即不以 / 开头),是相对于服务器 baseUrl 的路径。可以将所有 url 开头部分的相同路径写在 baseUrl 里(如下例中的 /hello/world 部分),如果以后这部分发生变化,可以在 baseUrl 里统一调整,无需分别调整每一个接口的 url。url 也可以写成绝对路径(以 / 开头),此时将忽略 baseUrl 的设置,适用于个别特殊的接口地址。例如:

{
  server: {
    baseUrl: 'http://10.22.11.10/hello/world/',
  },
  api: {
    api1: 'GET aaa',       //相对路径,请求地址:http://10.22.11.10/hello/world/aaa
    api2: 'GET aaa/bbb',   //相对路径,请求地址:http://10.22.11.10/hello/world/aaa/bbb
    api3: 'GET ../ccc',    //相对路径,请求地址:http://10.22.11.10/hello/ccc
    api4: 'GET ../../ddd', //相对路径,请求地址:http://10.22.11.10/ddd
    api5: 'GET /eee',      //绝对路径,请求地址:http://10.22.11.10/eee
    api6: 'GET /eee/fff',  //绝对路径,请求地址:http://10.22.11.10/eee/fff
  }
}

发送请求前后的处理

  • 前后处理函数可用于发送请求前后的公共处理,如格式化、加解密、跨域处理、会话管理、日志监控等
  • 前后处理函数都是异步函数,建议通过 async/await 语法来简化逻辑
  • 前后处理函数中传递的 options 对象和返回对象的格式依赖于底层请求库,并不统一,需要针对特定请求库进行处理
  • 同一个请求的前后处理函数中会传递相同且唯一的 requestId,它包括两部分,前半部分是 uuid 以保证唯一,后半部分是递增数字,可用来判定请求顺序
var ajax = new Ajax({
  /**
   * 前处理函数,在每个请求发送前执行,可以改变请求的数据、参数
   * @param {Object} data 上传数据
   * @param {Object} options 请求参数对象(格式由请求库决定)
   * @param {Object} api 接口配置参数
   * @param {Object} formData 上传数据的FormData对象(有上传文件时)
   * @param {String} requestId 请求ID,与responseFilter中同一个请求的requestId相同
   * @param {Object} res 包含 resove 和 reject 方法,具体用法见下面示例
   * @returns {Promise} 上传数据(格式由请求库及请求方式决定)
   */
  requestFilter: async function (data, options, api, formData, requestId, res) {
    //返回修改后的上传数据,如果有上传文件,且需要在这里统一修改上传数据时,需要直接修改formData对象
    return data;
    //下面两种用法可以阻止 ajax.request() 的默认请求,通过自定义请求,或构造模拟数据,直接返回结果或抛出异常
    //阻止继续发出请求,直接返回替代结果,由 ajax.request(...).then(res => {}) 获取
    return res.resolve({ hello: 'world' });
    //阻止继续发出请求,直接抛出异常,由 ajax.request(...).catch(err => {}) 捕获
    return res.reject(new Error('request error'));
  },
  /**
   * 后处理函数,在服务器返回请求后执行,可以改变返回给回调函数的数据
   * @param {Object} res 响应数据(格式由请求库决定)
   * @param {Object} xhr 请求xhr对象(仅jQuery库可用)
   * @param {Object} api 接口配置参数
   * @param {String} requestId 请求ID,与requestFilter中同一个请求的requestId相同
   * @returns {Promise} 响应数据(格式由请求库决定)
   */
  responseFilter: async function (res, xhr, api, requestId) {
    return res;
  },
  /**
   * 异常处理函数,在发生请求或响应异常后执行,可以对返回的异常对象属性值按需修改
   * @param {Object} err 异常对象(统一为 {code, msg} 或 resFormat 配置的格式)
   * @param {Object} api 接口配置参数
   * @param {String} requestId 请求ID,与requestFilter中同一个请求的requestId相同
   * @param {Object} requestOptions 请求选项,即 ajax.request() 的第三个参数
   * @returns {Object} 异常对象,如果返回 null,将不会继续触发请求方法的 catch 事件
   */
  errorFilter: async function (err, api, requestId, requestOptions) {
    return err;
  },
});
  • 使用示例:
//例1:针对jquery库,使用json格式提交上传数据
requestFilter: async function (data, options, api) {
  options.contentType = 'application/json; charset=UTF-8';
  options.processData = false;
  return JSON.stringify(data);
}
//例2:针对axios库,使用json格式提交上传数据
requestFilter: async function (data, options, api) {
  options.headers = {
    'Content-Type': 'application/json; charset=UTF-8',
  };
  return data;
}
//例3:针对axios库,在获取响应数据后记录登录token
responseFilter: async function (res) {
  if (res.headers.authorization) {
    window.sessionStorage.setItem('token', res.headers.authorization);
  }
  return res;
},

发送请求

ajax.request(apiId, data, options)
  • 第一个参数为接口 ID
  • 第二个参数为上传数据(若没有可省略),可以是 Plain Object 或表单 DOM 对象(提交的字段需设置 name 属性)
  • 第三个参数为其他选项(均可省略,详见以下说明)
  • 返回 Promise 对象
//options
{
  //请求超时时间,单位毫秒,此处的设置将覆盖服务器设置中的默认值
  timeout: 3000,
  //取消请求令牌(用法详见后面取消请求部分)
  cancelToken: ajax.cancelToken,
  //请求被取消后是否抛出异常
  //缺省为 false,即请求被取消后不需要做任何处理,直接忽略
  //如果设为 true,可以在 catch 中捕获到 err.code === 'abort' 的异常,可以有针对性地处理后续逻辑
  cancelRaiseError: true,
  //是否上传文件,如果在 data 部分添加了 File 或 FileList 对象,需要开启此参数
  //如果 data 直接提交表单 DOM 对象,则不需要设置此参数
  uploadFile: true,
  //上传进度事件(percent:0~100的百分比数值,loaded:已处理字节数,total:总字节数)
  onUploadProgress: (percent, loaded, total) => { console.log(`已上传 ${percent}%`); },
  //下载进度事件(percent:0~100的百分比数值,loaded:已处理字节数,total:总字节数)
  onDownloadProgress: (percent, loaded, total) => { console.log(`已下载 ${percent}%`); },
}
  • 如果 server 配置的 dataKey 非空,resolve 的数据只包含 dataKey 下的内容;否则是服务器返回的完整数据

  • 示例:

ajax.request('addUser', {
  name: '张三',
  age: 18,
}).then(res => {
  alert(`添加用户成功,新用户的ID为:${res.userId}`);
}).catch(err => {
  alert(`添加用户失败:${err.code} ${err.msg}`);
});

异常处理

  • 通过 ajax.request(...).catch(err => {...}) ,可以对单次请求进行异常处理。

  • 当请求发生异常时,无论是否进行了 catch,无论取消请求时是否设置了 cancelRaiseError 参数,都会始终执行 errorFilter 函数,所以可以在 errorFilter 函数里对所有请求的异常情况进行统一处理。errorFilter 函数应返回原始的异常对象,或返回经过处理的新的异常对象,这个异常对象将继续被发起请求的代码 ajax.request().catch() 处理。如果异常被 errorFilter 中的公共代码处理完以后,不需要再被发起请求的代码捕获并处理,让 errorFilter 返回 null 即可。

  • 以下七种情况会产生异常

    1. 因配置出错、JS脚本出错等原因,请求未能发出(错误码为'error');
    2. 请求已发出,但客户端在指定的超时时间内未接收到服务器响应、或因网络异常、页面被刷新等原因未能接收到响应(错误码为'timeout');
    3. 请求已发出,但接收到服务器返回的非 2xx 的 HTTP 响应码(错误码为 HTTP 响应码);
    4. HTTP 响应码为 2xx,但响应数据中的 codeKey 的值与 succCode 不符,即业务处理未成功(错误码为响应数据中 codeKey 的值);
    5. 因服务器返回的数据格式与期望不符,或在处理响应结果时出现其他异常,导致解析失败(错误码为'error');
    6. 客户端主动取消了请求,且 cancelRaiseError 参数设为了 true(错误码为'abort');
    7. 在 requestFilter 中通过 res.reject() 主动返回了异常,阻止了发送请求(错误码为'error')。
  • 如果要处理超时,需注意区分几种情况:一种是客户端请求超时(上述第 2 种),是指客户端发出请求后没有接收到服务器的响应,此时可能由于网络原因,服务器根本没有收到请求,也可能服务器已经收到了请求,但是处理时间过久没有及时返回,还可能是服务器已输出了响应数据,但由于网络等原因并未抵达客户端;当响应尚未返回时页面被刷新或跳转也可能会触发此异常。另一种情况是服务器处理超时,是因为服务器端也做了超时控制(一般是在网关处),如果服务器接收到请求后,内部处理时间过久,就返回一个前后端接口约定的代表服务器超时的 HTTP 响应码或业务返回码,此时产生的异常其实属于上述第 3 种或第 4 种情况。还有一种服务器登录会话超时,在客户端与服务器长时间未进行交互,导致 session 或 token 失效后发生,也属于上述第 3 种或第 4 种情况,需根据与后台接口约定的规则处理。

  • 无论服务器 format 选项(代表服务器实际返回的数据格式)中配置的 codeKeymsgKey 是什么,在 catch 到的 Error 对象上均被统一为 resFormat 选项(代表业务代码中获取到的数据格式)中配置的键名(缺省为 'code''msg'),这样如果将来服务器返回数据格式发生变化,业务代码也无需修改。

  • Error 对象中可能还会包含服务器返回的原始响应数据,其键名由 resFormat.dataKey 配置决定,如果该配置项为空,则键名固定为 data。只有在接收到服务器响应数据之后出现的异常,Error 对象中才会包含这些数据,如果是发送请求之前出现的异常,或请求被取消,或超时未接收到响应,Error 对象中都不会包含响应数据。

上传文件

  • 文件需要通过 <input type="file"> 元素来选择
  • 添加 multiple 属性 <input type="file" multiple>,可以允许同时选择多个文件
  • 添加 accept 属性,可以限制可选择的文件类型,例如:
    • <input type="file" accept="image/png">(只接受 png 图片)
    • <input type="file" accept=".png">(同上)
    • <input type="file" accept="image/png,image/jpeg">(接受 png 和 jpeg 图片)
    • <input type="file" accept=".png,.jpg,.jpeg">(同上)
    • <input type="file" accept="image/*">(接受任意图片类型)
    • <input type="file" accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document">(接受 office 文档类型)
  • 通过 input.files 可以获取已选择的文件(FileList 对象,通过下标可以选择每一个 File 对象)
  • 可以直接将 File 或 FileList 对象放入上传文件,同时也可以传入其他文本字段值,这种用法需要设置 uploadFile: true 选项,例如:
ajax.request('uploadApi', {
  file1: form.file1.files[0],
  fileDesc1: form.fileDesc1.value,
  file2: form.file2.files,
  fileDesc2: form.fileDesc2.value,
}, {uploadFile: true}).then(res => {
  alert(`上传文件成功:${JSON.stringify(res)}`);
}).catch(err => {
  alert(`上传文件失败:${err.code} ${err.msg}`)
});
  • 也可以直接传入整个表单的 DOM 对象,表单中带有 name 属性的字段均会被提交,这种用法不需要设置 uploadFile 选项,例如:
document.forms[0].onsubmit = function (event) {
  event.preventDefault();
  ajax.request('uploadApi', event.target).then(function (res) {
    alert(`上传文件成功:${JSON.stringify(res)}`);
  }, function (res) {
    alert(`上传文件失败:${err.code} ${err.msg}`)
  });
};

取消请求

  • 先用 ajax.cancelToken 申请取消请求令牌,通过 ajax.request() 的第三个参数的 cancelToken 选项传入,然后在需要取消的时候执行 ajax.cancel(token)
  • 如果设置了 cancelRaiseError: true,则取消请求后将抛出异常,否则将被忽略,不会执行任何回调
//申请取消请求令牌
var cancelToken = ajax.cancelToken;
ajax.request('testApi', {}, {
  cancelToken, 
  cancelRaiseError: true,
}).then(res => {
  alert('请求成功返回:' + JSON.stringify(res));
}).catch(err => {
  if (err.code === 'abort') {
    alert('请求被取消了');
  }
  else {
    alert(`请求发生异常:${err.code} ${err.msg}`);
  }
});
//这里模拟一段时间后取消请求
setTimeout(function () {
  ajax.cancel(cancelToken);
}, 1000);

取消全部请求

  • 无需申请取消令牌,直接调用 ajax.cancelAll(); 即可取消未完成的全部请求
  • 此方法一般用在销毁组件、关闭对话框、前端路由切换等事件发生时,防止未完成的请求在不恰当的时候触发回调函数
  //针对axios库,直接调用即可
  ajax.cancelAll();

  //针对jQuery库,需要在请求发起后才能取消。如果在发起请求的同一个同步过程中执行取消动作,
  //可能会取消失败,通过 setTimeout 0 将取消动作延迟到下一个事件循环可以解决这个问题
  setTimeout(function () {
    ajax.cancelAll();
  }, 0);

获取接口信息

  • 根据接口ID查询接口和服务器配置信息
let config = ajax.getApi(apiId);

//输出结果格式:
{
  //请求方法
  "method": "POST",
  //请求地址(已根据baseUrl计算出完整地址,但url变量不会被替换)
  "url": "http://127.0.0.1:9666/demoApi/hello/world/:ccc",
  //静态上传字段值
  "key": {
    "xxx": 888,
    "yyy": 999
  },
  //服务器配置
  "server": {
    "baseUrl": "/demoApi/",
    "method": "GET",
    "timeout": 0,
    "format": {
      "codeKey": "returnCode",
      "msgKey": "message",
      "dataKey": "returnData",
      "succCode": 0
    }
  },
  //接口ID
  "apiId": "hello",
  //服务器ID
  "serverId": "demo"
}