├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── example
└── defaultConfig
│ └── request.js
├── index.d.ts
├── package.json
├── rollup.config.js
├── src
├── InterceptorManager.js
├── class.js
├── core
│ └── dispatchRequest.js
├── defaults.js
├── helpers
│ └── util.js
├── index.js
└── request.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "node": true,
6 | },
7 | globals : {
8 | wx:true,
9 | },
10 | "extends": "eslint:recommended",
11 | "parserOptions": {
12 | "ecmaVersion": 12,
13 | "sourceType": "module"
14 | },
15 | "rules": {
16 | semi: [2, "always"],
17 | quotes: [2, "double"],
18 | indent: ["error", 4],
19 | "no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
20 | camelcase: 0,
21 | "no-irregular-whitespace": ["error", { "skipComments": true }],
22 | "no-tabs" : 0
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 |
7 | # Editor directories and files
8 | .idea
9 | *.suo
10 | *.ntvs*
11 | *.njsproj
12 | *.sln
13 | /dist
14 | *.tgz
15 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "singleQuote": false,
4 | "printWidth": 100,
5 | "semi": true,
6 | "useTabs": false
7 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # wechat-request
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | > 基于Promise微信小程序http请求,轻便,小巧,api友好,功能丰富
14 |
15 |
16 | ## 特别之处
17 | - 支持Promise API
18 | - 拦截请求和响应
19 | - 转换请求和响应数据
20 | - 取消请求
21 | - 自动转换为JSON数据
22 | - 超时请求
23 | - 告别callback
24 | - 支持默认请求前缀
25 | - 支持并发请求
26 |
27 | ## 使用方式
28 |
29 | ```yarn add wechat-request```
30 | ```npm install wechat-request --save ```
31 | ```import wxRequest from 'wechat-request';```
32 |
33 |
34 |
35 | ## 一步上手
36 |
37 | 首先来一个简单的```get```请求
38 | ```js
39 | // 向具有给定ID的用户发出请求
40 | wxRequest.get('/user?id=12345')
41 | .then(function (response) {
42 | console.log(response);
43 | })
44 | .catch(function (error) {
45 | console.log(error);
46 | });
47 |
48 | // 可选地,上面的请求也可以按照
49 | wxRequest.get('/user', {
50 | params: {
51 | id: 'number'
52 | }
53 | }).then(function (response) {
54 | console.log(response);
55 | }).catch(function (error) {
56 | console.log(error);
57 | });
58 |
59 | // 想要使用 async/await? 将`async`关键字添加到外部函数/method
60 | async function getUser() {
61 | try {
62 | const response = await wxRequest.get('/user?ID=12345');
63 | console.log(response);
64 | } catch (error) {
65 | console.error(error);
66 | }
67 | }
68 | ```
69 | > 多种方法使用async/waait,开启代码便捷、畅快之旅
70 |
71 | 接着再来一个```post```请求
72 |
73 | ```js
74 | wxRequest.post('/user', {
75 | firstname : 'firstname',
76 | lastname : 'lastname'
77 | }).then(function (response) {
78 | console.log(response);
79 | }).catch(function (error) {
80 | console.log(error);
81 | });
82 | ```
83 |
84 | 执行多并发请求例子
85 |
86 | ```js
87 | function getUserAccount() {
88 | return wxRequest.get('/user/12345');
89 | }
90 |
91 | function getUserPermissions() {
92 | return wxRequest.get('/user/12345/permissions');
93 | }
94 |
95 | wxRequest.all([getUserAccount(), getUserPermissions()])
96 | .then(response =>{
97 | // dosoming ...
98 | });
99 | ```
100 |
101 | ## 请求方法别名
102 | 当然除了常见的```get```,```post```其他的请求也统一封装
103 |
104 | - ```wxRequest.request(config)```
105 | - ```wxRequest.get(url[, config])```
106 | - ```wxRequest.delete(url[, config])```
107 | - ```wxRequest.head(url[, config])```
108 | - ```wxRequest.options(url[, config])```
109 | - ```wxRequest.post(url[, data[, config]])```
110 | - ```wxRequest.put(url[, data[, config]])```
111 | - ```wxRequest.patch(url[, data[, config]])```
112 |
113 | > note: 当使用别名方法`url`时,`method`和`data`属性不需要在config中指定。
114 |
115 |
116 | ### 全局配置
117 |
118 | 使用场景用户请求需要token,或者地址前缀,一次配置,省时省心。
119 |
120 | ```js
121 | wxRequest.defaults.baseURL = 'https://api.example.com';
122 | wxRequest.defaults.headers['Authorization'] = AUTH_TOKEN;
123 | wxRequest.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
124 | ```
125 |
126 | ## 致谢 && 参考
127 | * [axios](https://github.com/axios/axios)
128 |
129 |
130 | ## License
131 |
132 | MIT
133 |
--------------------------------------------------------------------------------
/example/defaultConfig/request.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import wxRequest from 'wechat-request';
4 |
5 |
6 | // 针对post请求增加token
7 | wxRequest.interceptors.request.use(
8 | config => {
9 | if (config.method === 'post') {
10 | config.headers.common['Authorization'] = AUTH_TOKEN;
11 | }
12 | return config;
13 | },
14 | err => {
15 | return Promise.reject(err);
16 | });
17 |
18 |
19 |
20 | export default wxRequest;
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | export interface BaseData {
2 | [key: string]: any;
3 | }
4 |
5 | export interface RequestPromise extends Promise> {
6 |
7 | }
8 |
9 | export type Method =
10 | | 'get' | 'GET'
11 | | 'delete' | 'DELETE'
12 | | 'head' | 'HEAD'
13 | | 'options' | 'OPTIONS'
14 | | 'post' | 'POST'
15 | | 'put' | 'PUT'
16 | | 'patch' | 'PATCH'
17 | | 'link' | 'LINK'
18 | | 'unlink' | 'UNLINK'
19 |
20 | export interface RequestConfig {
21 | url?: string;
22 | method?: Method;
23 | baseURL?: string;
24 | headers?: BaseData;
25 | params?: BaseData;
26 | data?: BaseData;
27 | timeout?: number;
28 | }
29 |
30 | export interface InterceptorManager {
31 | use(onFulfilled?: (value: V) => V | Promise, onRejected?: (error: any) => any): number;
32 | eject(id: number): void;
33 | }
34 |
35 | export interface Response {
36 | data: T,
37 | status: number,
38 | statusText: string,
39 | headers: T,
40 | config: RequestConfig
41 | }
42 |
43 | export interface RequestError extends Error {
44 |
45 | }
46 |
47 | export interface Instance {
48 | (config: RequestConfig): RequestPromise;
49 | (url: string, config?: RequestConfig): RequestPromise;
50 | interceptors: {
51 | request: InterceptorManager;
52 | response: InterceptorManager;
53 | };
54 | request>(config: RequestConfig): Promise;
55 | get>(url: string, config?: RequestConfig): Promise;
56 | delete>(url: string, config?: RequestConfig): Promise;
57 | head>(url: string, config?: RequestConfig): Promise;
58 | post>(url: string, data?: any, config?: RequestConfig): Promise;
59 | put>(url: string, data?: any, config?: RequestConfig): Promise;
60 | patch>(url: string, data?: any, config?: RequestConfig): Promise;
61 | }
62 | export interface RequestStatic extends Instance {
63 | create(config?: RequestConfig): Instance;
64 | all(values: (T | Promise)[]): Promise;
65 | }
66 |
67 | declare const wechatRequest: RequestStatic
68 | export default wechatRequest;
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wechat-request",
3 | "version": "2.7.0",
4 | "description": "基于Promise微信小程序http请求,轻便,小巧,api友好,功能丰富",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "dev": "rollup -c -w",
8 | "build": "rollup -c",
9 | "lint": "eslint -c ./.eslintrc.js --ext .js src/ --fix",
10 | "lint-prettire": "prettier --write src/**/*.{vue,js,jsx,less}"
11 | },
12 | "keywords": [
13 | "request",
14 | "wechat",
15 | "promise",
16 | "http",
17 | "微信小程序"
18 | ],
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/hatedMe/wechat-request.git"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/hatedMe/wechat-request/issues"
25 | },
26 | "license": "ISC",
27 | "author": "Atom <7548764@qq.com>",
28 | "files": [
29 | "dist",
30 | "index.d.ts"
31 | ],
32 | "types": "index.d.ts",
33 | "devDependencies": {}
34 | }
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | input: "src/index.js",
3 | output: {
4 | file: "dist/index.js",
5 | format: "umd",
6 | name: "wechatRequest",
7 | },
8 | watch: {
9 | include: "src/**",
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/src/InterceptorManager.js:
--------------------------------------------------------------------------------
1 | export default class InterceptorManager {
2 | constructor() {
3 | this.handlers = [];
4 | }
5 |
6 | use(fulfilled, rejected) {
7 | this.handlers.push({
8 | fulfilled,
9 | rejected,
10 | });
11 | return this.handlers.length - 1;
12 | }
13 |
14 | eject(id) {
15 | if (this.handlers[id]) {
16 | this.handlers[id] = null;
17 | }
18 | }
19 |
20 | forEach(fn) {
21 | this.handlers.forEach((e) => {
22 | if (e !== null) {
23 | fn(e);
24 | }
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/class.js:
--------------------------------------------------------------------------------
1 | import * as util from "./helpers/util";
2 | import InterceptorManager from "./InterceptorManager";
3 | import { dispatchRequest } from "./core/dispatchRequest";
4 |
5 | class Request {
6 | constructor(config) {
7 | this.defaults = config;
8 | this.interceptors = {
9 | request: new InterceptorManager(),
10 | response: new InterceptorManager(),
11 | };
12 | }
13 | request(config) {
14 | if (typeof config === "string") {
15 | config = util.merge({ url: arguments[0] }, arguments[1]);
16 | }
17 |
18 | config = util.deepMerge(this.defaults, config);
19 | config.method = config.method ? config.method.toLowerCase() : "get";
20 |
21 | let chain = [dispatchRequest, undefined];
22 | let promise = Promise.resolve(config);
23 |
24 | this.interceptors.request.forEach(function (interceptor) {
25 | chain.unshift(interceptor.fulfilled, interceptor.rejected);
26 | });
27 |
28 | this.interceptors.response.forEach(function (interceptor) {
29 | chain.push(interceptor.fulfilled, interceptor.rejected);
30 | });
31 |
32 | while (chain.length) {
33 | promise = promise.then(chain.shift(), chain.shift());
34 | }
35 |
36 | return promise;
37 | }
38 | all(promises) {
39 | return Promise.all(promises);
40 | }
41 | }
42 |
43 | ["delete", "get", "head", "options", "trace"].forEach((method) => {
44 | Request.prototype[method] = function (url, config) {
45 | return this.request(
46 | util.merge(config || {}, {
47 | method,
48 | url,
49 | })
50 | );
51 | };
52 | });
53 |
54 | ["post", "put", "patch"].forEach((method) => {
55 | Request.prototype[method] = function (url, data, config) {
56 | return this.request(
57 | util.merge(config || {}, {
58 | method,
59 | url,
60 | data,
61 | })
62 | );
63 | };
64 | });
65 |
66 | export default Request;
67 |
--------------------------------------------------------------------------------
/src/core/dispatchRequest.js:
--------------------------------------------------------------------------------
1 | import * as util from "../helpers/util";
2 | export const dispatchRequest = function (config) {
3 | if (config.baseURL && !util.isAbsoluteURL(config.url)) {
4 | config.url = util.combineURLs(config.baseURL, config.url);
5 | }
6 |
7 | config.url = util.buildURL(config.url, config.params);
8 |
9 | config.data = util.merge(config.data, config.transformRequest(config.data));
10 |
11 | config.headers = util.merge(
12 | config.headers.common || {},
13 | config.headers[config.method] || {},
14 | config.headers || {}
15 | );
16 |
17 | let methods = ["delete", "get", "head", "post", "put", "patch", "common"];
18 | methods.forEach((method) => {
19 | delete config.headers[method];
20 | });
21 |
22 | let promise = Promise.resolve(config);
23 | promise = promise.then((config) => {
24 | return new Promise(function (resolve, reject) {
25 | let requestTask = wx.request({
26 | url: config.url,
27 | data: util.buildData(config.data),
28 | header: config.headers,
29 | method: config.method,
30 | dataType: config.dataType,
31 | success: function (res) {
32 | resolve({
33 | data: res.data,
34 | headers: res.header,
35 | status: res.statusCode,
36 | statusText: "ok",
37 | });
38 | },
39 | fail: function (err) {
40 | reject(err);
41 | },
42 | complete: function () {
43 | config.complete && config.complete();
44 | },
45 | });
46 |
47 | if (config.timeout && typeof config.timeout === "number" && config.timeout > 1000) {
48 | setTimeout(() => {
49 | requestTask.abort();
50 | resolve({
51 | status: "canceled",
52 | });
53 | }, config.timeout);
54 | }
55 | });
56 | });
57 |
58 | return promise;
59 | };
60 |
--------------------------------------------------------------------------------
/src/defaults.js:
--------------------------------------------------------------------------------
1 | import * as util from "./helpers/util";
2 |
3 | let DEFAULT_CONTENT_TYPE = {
4 | "Content-Type": "application/x-www-form-urlencoded",
5 | };
6 |
7 | const defaults = {
8 | method: "get", // default
9 | // baseURL: '',
10 | dataType: "json",
11 | responseType: "text",
12 | // timeout: 0,
13 | headers: {},
14 |
15 | // params : {},
16 |
17 | transformRequest(data) {
18 | return data;
19 | },
20 |
21 | // transformResponse (data) {
22 | // return data;
23 | // },
24 |
25 | // validateStatus ( status ) {
26 | // return status >= 200 && status < 300
27 | // }
28 | };
29 |
30 | defaults.headers = {
31 | common: {
32 | Accept: "application/json, text/plain, */*",
33 | },
34 | };
35 |
36 | ["delete", "get", "head", "post", "put", "patch"].map((e) => {
37 | defaults.headers[e] = util.merge(defaults.headers, DEFAULT_CONTENT_TYPE);
38 | });
39 |
40 | export default defaults;
41 |
--------------------------------------------------------------------------------
/src/helpers/util.js:
--------------------------------------------------------------------------------
1 | export const bind = function (fn, thisArg) {
2 | return function warp() {
3 | return fn.apply(thisArg, Array.from(arguments));
4 | };
5 | };
6 |
7 | export const extend = function (a, b, thisArg) {
8 | let o = Object.getOwnPropertyNames(b);
9 | o.forEach((attr) => {
10 | if (thisArg && typeof b[attr] === "function") {
11 | a[attr] = bind(b[attr], thisArg);
12 | } else {
13 | a[attr] = b[attr];
14 | }
15 | });
16 | return a;
17 | };
18 |
19 | export const copyobj = function (a, b) {
20 | return Object.assign({}, a, b);
21 | };
22 |
23 | export const merge = function () {
24 | var result = {};
25 | Array.from(arguments).forEach((e) => {
26 | for (let key in e) {
27 | if (e[key] && typeof e[key] === "object" && !isEmptyObject(e[key])) {
28 | merge(result[key], e[key]);
29 | }
30 | result[key] = e[key];
31 | }
32 | });
33 | return result;
34 | };
35 |
36 | export const deepMerge = function () {
37 | let result = {};
38 | Array.from(arguments).forEach((e) => {
39 | if (e && typeof e === "object" && !isEmptyObject(e)) {
40 | Object.keys(e).forEach((key) => {
41 | if (e[key] && typeof e[key] === "object") {
42 | result[key] = deepMerge(result[key], e[key]);
43 | }
44 | result[key] = e[key];
45 | });
46 | }
47 | });
48 | return result;
49 | };
50 |
51 | export const isEmptyObject = (obj) => {
52 | return Object.getOwnPropertyNames(obj).length === 0;
53 | };
54 |
55 | export const isObject = (obj) => {
56 | return obj !== null && typeof obj === "object";
57 | };
58 |
59 | export const combineURLs = function (baseURL, relativeURL) {
60 | return relativeURL
61 | ? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
62 | : baseURL;
63 | };
64 |
65 | function encode(val) {
66 | return encodeURIComponent(val)
67 | .replace(/%40/gi, "@")
68 | .replace(/%3A/gi, ":")
69 | .replace(/%24/g, "$")
70 | .replace(/%2C/gi, ",")
71 | .replace(/%20/g, "+")
72 | .replace(/%5B/gi, "[")
73 | .replace(/%5D/gi, "]");
74 | }
75 | export const buildURL = function (url, paramsObject) {
76 | if (!paramsObject || isEmptyObject(paramsObject)) return url;
77 | let parts = [];
78 | Object.keys(paramsObject).forEach((key) => {
79 | parts.push(encode(key) + "=" + encode(paramsObject[key]));
80 | });
81 | return (url += (url.indexOf("?") === -1 ? "?" : "&") + parts.join("&"));
82 | };
83 |
84 | export const isAbsoluteURL = function (url) {
85 | return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
86 | };
87 |
88 | export const buildData = (data) => {
89 | if (!isObject(data) || isEmptyObject(data)) return {};
90 | const result = {};
91 | Object.keys(data).forEach((key) => {
92 | if (data[key] !== null && typeof data[key] !== "undefined") {
93 | result[key] = data[key];
94 | }
95 | });
96 | return result;
97 | };
98 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import request from "./request";
2 | export default request;
3 |
--------------------------------------------------------------------------------
/src/request.js:
--------------------------------------------------------------------------------
1 | import Request from "./class";
2 | import * as util from "./helpers/util";
3 | import defaults from "./defaults";
4 |
5 | function createInstance(config) {
6 | let context = new Request(config);
7 | let instance = util.bind(Request.prototype.request, context);
8 | util.extend(instance, Request.prototype, context);
9 | util.extend(instance, context);
10 | // 用于创建多个实例
11 | instance.create = function (config) {
12 | return createInstance(util.merge(defaults, config));
13 | };
14 | return instance;
15 | }
16 |
17 | const request = createInstance(defaults);
18 | // 并发请求数据处理
19 | request.spread = function (callback) {
20 | return function (...arg) {
21 | return callback.apply(null, [...arg]);
22 | };
23 | };
24 |
25 | export default request;
26 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------