├── .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 | --------------------------------------------------------------------------------