├── LICENSE ├── README.md ├── app.js ├── app.json ├── app.wxss ├── demo.png ├── image └── warn.png ├── pages └── index │ ├── index.js │ ├── index.wxml │ └── index.wxss ├── project.config.json └── util ├── network.js └── service.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 23hp 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 | 使用 Promise 封装的微信小程序网络请求库,更少代码,更方便定制 4 | 5 | ![请求示例](https://github.com/23hp/wx_network/blob/master/demo.png) 6 | 7 | ### 功能 8 | 9 | - 减少你70%的代码量,不再一遍遍重复微信的样板代码 10 | - 调用灵活,错误处理简单而方便 11 | - 支持并发/并行执行多个请求 12 | - 自动打印请求日志 13 | - 支持对请求的发起和响应进一步定制,实现固定参数、自动提取返回值指定数据、自动处理响应错误如token过期自动跳转的功能。 14 | 15 | ### 与微信原生请求库对比 16 | 17 | 微信现有请求方法 18 | 19 | let that = this; 20 | wx.request({ 21 | url: SERVER_ROOT + '/user/authorization', 22 | method: "POST", 23 | header: { 24 | 'content-type': 'application/x-www-form-urlencoded' 25 | }, 26 | data: { 27 | code: res.code 28 | }, 29 | success(data){ 30 | console.log(data); 31 | if(data.status && data.status.succeed==1) { 32 | that.setData({ 33 | userInfo:data.data.userInfo 34 | }); 35 | }else { 36 | wx.showToast({ 37 | title: "获取数据失败" 38 | }); 39 | } 40 | }, 41 | fail(){ 42 | wx.showToast({ 43 | title: "连接服务器失败" 44 | }); 45 | } 46 | }) 47 | 48 | 使用此库后 49 | 50 | authorization(res.code).then(data=>{ 51 | this.setData({ 52 | userInfo:data.userInfo 53 | }); 54 | }).catch(e=>{ 55 | wx.showToast({ 56 | title: "连接服务器失败" 57 | }); 58 | }) 59 | 60 | `authorization`是经过封装的请求方法,一次封装可多处调用。可以看到,原本27行代码量,减少到了8行。下面我们来看如何使用,并定制自己的请求方法。 61 | 62 | ### 请求示例 63 | 64 | #### 单个请求 65 | 66 | getPhoto(1).then(data => this.setData({photo1: data})); 67 | 68 | #### 带加载框和错误提示 69 | 70 | wx.showLoading({title: '加载中'}); 71 | getPhoto(2).then(data => { 72 | this.setData({photo1: data}); 73 | wx.hideLoading(); 74 | // throw '我出错了!' //todo 你可以尝试抛出一个异常 75 | }).catch(e => { 76 | wx.showToast({title: '请求失败'}); 77 | }) 78 | 79 | #### 多个请求(顺序请求) 80 | 81 | getPhoto(3).then(data => { 82 | this.setData({photo1: data}); 83 | return getPhoto(4); //下一个请求 84 | }).then(data =>{ 85 | this.setData({photo2: data}) 86 | }); 87 | 88 | #### 多个请求(同时请求) 89 | 90 | Promise.all([getPhoto(5), getPhoto(6)]).then(listData => { 91 | this.setData({ 92 | photo1: listData[0], 93 | photo2: listData[1], 94 | }); 95 | }); 96 | 97 | 98 | ### 目录说明 99 | - example 微信小程序的演示项目, 100 | - util 封装的请求库。 101 | 102 | ### 使用方法 103 | 104 | 跟着本教程一同练习,你马上就能上手。 105 | 106 | 1. 复制util目录下的`network.js`和`service.js`文件到你的项目目录,network.js 存放原始的请求方法,service存放接口。 107 | 2. 按需引入`service.js`中相应请求方法并调用。 108 | 109 | ### 定义接口 110 | 111 | 在service.js中定义请求方法 112 | 113 | import {get, post} from './network'; 114 | 115 | const API_ROOT = 'https://jsonplaceholder.typicode.com'; //服务器根路径 116 | 117 | //请求图片 118 | export function getPhoto(id){ 119 | const url = API_ROOT + '/photos/' + id; 120 | return get(url); 121 | } 122 | 123 | ### 引入和调用 124 | 125 | import {getPhoto} from '../../util/service'; 126 | 127 | onLoad(){ 128 | getPhoto(1).then(data =>{ 129 | this.setData({photo1: data})); 130 | } 131 | }, 132 | 133 | 134 | 使用then方法就可以获取到返回值了,如果需要处理错误,再在后面接catch方法,详见上面第二个请求示例。 135 | 136 | 教程到这里就结束了,是不是很简单? 137 | 138 | ### 进一步封装示例 139 | 140 | 也许你还想要一些高级特性,比如说处理诸如固定参数、自动提取返回值指定数据、自动处理响应错误、登录凭证过期提示用户并自动跳转等功能。那你就会用到下面的进阶教程了。 141 | 142 | #### 例1:固定参数 143 | 144 | 某些接口需要固定传参,比如说平台标识、用户凭证、特定的header等等。如果每次调用都要手动传入,不仅麻烦,而且也不利于修改。 145 | 146 | 以接口要传入指定header参数为例,有了`network.js`这层封装,现在我们可以这样做: 147 | 148 | 找到`network.js`下的这段代码: 149 | 150 | 151 | /** 152 | * 接口请求基类方法 153 | * @param method 请求方法 必填 154 | * @param url 请求路径 必填 155 | * @param data 请求参数 156 | * @param header 请求头 157 | * @returns {Promise} 158 | */ 159 | export function request(method, url, data, header = {'Content-Type': 'application/json'}) { 160 | console.info(method, url); 161 | return new Promise((resolve, reject) => { 162 | const response = {}; 163 | wx.request({ 164 | url, method, data, header, 165 | success: (res) => response.success = res.data, 166 | fail: (error) => response.fail = error, 167 | complete: () => { ... }, 168 | }); 169 | }); 170 | } 171 | 172 | > 这里我们把微信的wx.request API封装成了自定义request方法,返回了一个Promise对象。我们的get、post、put、delete最终调用的都是这个方法。 173 | 174 | 我们要定义该方法的第四个参数header(请求头),它有一个默认值`header = {'Content-Type': 'application/json'}`,当我们没有传入header时,它会自动使用默认值,我们可以直接改变这个值: 175 | 176 | export function request(method, url, data, header = 'Content-Type': 'application/json'}) { 177 | ... 178 | } 179 | 180 | => 181 | 182 | export function request(method, url, data, header ={'Content-Type': 'application/x-www-form-urlencoded'}) { 183 | ... 184 | } 185 | 186 | 这样,所有接口将会使用新的默认header参数进行请求了。 187 | 188 | #### 例2:自动解析数据 189 | 服务器返回的数据往往有某种固定格式,需要我们做一些变换才能使用。拿一个典型的返回值例子来说: 190 | 191 | { 192 | "code":1, 193 | "data":{ 194 | "name":"凯" 195 | }, 196 | "message":"" 197 | } 198 | 199 | 我们拿到数据首先要**判断`code`值是否为`1`**,才能正常取出需要的`data`里面的字段,不为`1`则要做错误处理。假如每次都对返回值做判断,我们的代码会变得更加凌乱和难于维护。 200 | 还是在`network.js`方法里: 201 | 202 | export function request(method, url, data, header = {'Content-Type': 'application/json'}) { 203 | console.info(method, url); 204 | return new Promise((resolve, reject) => { 205 | const response = {}; 206 | wx.request({ 207 | url, method, data, header, 208 | success: (res) => { 209 | if (res.data.code === 1) { //判断code值 210 | response.success = res.data.data; // 你将在then方法中看到的数据 211 | } else { 212 | response.fail = res.data; // 你将在catch方法中接收到该错误 213 | } 214 | }, 215 | fail: (error) => response.fail = error, 216 | complete: () => { ... }, 217 | }); 218 | }); 219 | } 220 | 221 | 通过改造,我们再次调用`service.js`中定义的方法将会直接得到正确的数据。 222 | 223 | #### 例3:通用错误处理 224 | 服务器返回的错误往往有规律可循,以下面这段返回值为例: 225 | 226 | { 227 | "code":-2, 228 | "data": null, 229 | "message":"登录已过期" 230 | } 231 | 232 | 接前面的例子,当`code`值不为`1`时,`message`字段返回异常信息,不同的`code`值我们要做不同的处理,处理方法跟例2相近。 233 | 234 | 这里假设`code`为`-2`时,代表登录过期,需要跳转到登录页 235 | 236 | export function request(method, url, data, header = {'Content-Type': 'application/json'}) { 237 | console.info(method, url); 238 | return new Promise((resolve, reject) => { 239 | const response = {}; 240 | wx.request({ 241 | url, method, data, header, 242 | success: (res) => { 243 | if (res.data.code === 1) { 244 | response.success = res.data.data; // 你将在then方法中看到的数据 245 | } else if (res.data.code === -2) { 246 | wx.navigateTo({url:'/pages/login/login'}); // 跳去登录页 247 | } else { 248 | response.fail = res.data; // 你将在catch方法中接收到该错误 249 | } 250 | }, 251 | fail: (error) => response.fail = error, 252 | complete: () => { ... }, 253 | }); 254 | }); 255 | } 256 | 257 | 这样,当任何一个返回的`code`值为`-2`时,小程序都会跳到登录页了。 258 | 259 | - - - 260 | 261 | 教程到此结束,对你有所帮助的话,顺手给个 ♥ 吧! 欢迎你提出建议,或和我一起完善代码! -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({}) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "pages/index/index" 4 | ], 5 | "window":{ 6 | "backgroundTextStyle":"light", 7 | "navigationBarBackgroundColor": "#fff", 8 | "navigationBarTitleText": "微信网络封装", 9 | "navigationBarTextStyle":"black" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | 3 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/23hp/wx_network/7083054c1e7eae86ccb553035cc3cfc5c9a2ef43/demo.png -------------------------------------------------------------------------------- /image/warn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/23hp/wx_network/7083054c1e7eae86ccb553035cc3cfc5c9a2ef43/image/warn.png -------------------------------------------------------------------------------- /pages/index/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | //引入接口 3 | import {getPhoto} from "../../util/service"; 4 | 5 | Page({ 6 | data: { 7 | photo1: null, 8 | photo2: null, 9 | }, 10 | //数据清零 11 | reset(){ 12 | this.setData({ 13 | photo1: null, 14 | photo2: null, 15 | }) 16 | }, 17 | // 单个请求 18 | loadOne() { 19 | this.reset(); //数据清零 20 | getPhoto(1).then(data => this.setData({photo1: data})); 21 | }, 22 | // 带加载框和错误提示 23 | loadWithDialog() { 24 | this.reset(); 25 | wx.showLoading({title: '加载中'}); 26 | getPhoto(2).then(data => { 27 | this.setData({photo1: data}); 28 | wx.hideLoading(); 29 | // throw '我出错了!' //todo 你可以尝试抛出一个异常 30 | }).catch(e => { 31 | wx.showToast({title: '请求失败', image: '/image/warn.png'}); 32 | console.error('请求失败',e); 33 | }) 34 | }, 35 | // 多个请求(顺序请求) 36 | loadOneByOne() { 37 | this.reset(); 38 | getPhoto(3) 39 | .then(data => { 40 | this.setData({photo1: data}); 41 | return getPhoto(4); 42 | } 43 | ).then(data => 44 | this.setData({photo2: data}) 45 | ); 46 | 47 | }, 48 | // 多个请求(同时请求) 49 | loadMany() { 50 | this.reset(); 51 | Promise.all([getPhoto(5), getPhoto(6)]).then(listData => { 52 | this.setData({ 53 | photo1: listData[0], 54 | photo2: listData[1], 55 | }); 56 | }); 57 | }, 58 | }); 59 | -------------------------------------------------------------------------------- /pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 发送请求: 3 | 4 | 5 | 6 | 7 | 8 | 9 | 返回值: 10 | 11 | 12 | 13 | {{photo1.title}} 14 | 15 | 16 | 17 | {{photo2.title}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | page{ 3 | padding: 30rpx; 4 | display: flex; 5 | flex-direction: column; 6 | box-sizing: border-box; 7 | color: #ddd; 8 | background-color: rgb(60, 63, 65); 9 | font-family: 微软雅黑,"Microsoft YaHei", Helvetica Neue, Helvetica, sans-serif; 10 | } 11 | button{ 12 | margin-right: 8rpx; 13 | } 14 | image{ 15 | width: 90%; 16 | } 17 | .marginTop20{ 18 | margin-top: 20rpx; 19 | } 20 | .result{ 21 | width: 100%; 22 | font-size: 26rpx; 23 | padding: 12px 0; 24 | } 25 | .photo{ 26 | width: 300rpx; 27 | } -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件。", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": true, 9 | "postcss": true, 10 | "minified": true, 11 | "newFeature": true 12 | }, 13 | "compileType": "miniprogram", 14 | "libVersion": "1.9.98", 15 | "appid": "touristappid", 16 | "projectname": "wx_network", 17 | "condition": { 18 | "search": { 19 | "current": -1, 20 | "list": [] 21 | }, 22 | "conversation": { 23 | "current": -1, 24 | "list": [] 25 | }, 26 | "game": { 27 | "currentL": -1, 28 | "list": [] 29 | }, 30 | "miniprogram": { 31 | "current": -1, 32 | "list": [] 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /util/network.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by 23hp on 2018/3/30. 3 | * 基于Promise的网络请求库,包含GET POST请求,上传下载功能 4 | * 使用方法: 5 | * 先引入: import {get,post,...} from 本文件; 6 | * · get请求: get("/index",{id:2}).then(data=>{}).catch(error=>{}); 7 | * · post请求: post("/index",{id:2}).then(data=>{}).catch(error=>{}); 8 | * Promise详细介绍: 9 | * http://es6.ruanyifeng.com/#docs/promise 10 | */ 11 | 12 | /** 13 | * 发起get请求 14 | * @param url 请求路径 必填 15 | * @param data 请求参数 get请求的参数会自动拼到地址后面 16 | * @param headers 请求头 选填 17 | * @returns {Promise} 18 | */ 19 | export const get = (url, data, headers) => request('GET', url, data, headers); 20 | 21 | /** 22 | * 发起post请求 23 | * @param url 请求路径 必填 24 | * @param data 请求参数 25 | * @param headers 请求头 选填 26 | * @returns {Promise} 27 | */ 28 | export const post = (url, data, headers) => request('POST', url, data, headers); 29 | /** 30 | * 发起put请求 31 | * @param url 请求路径 必填 32 | * @param data 请求参数 33 | * @param headers 请求头 选填 34 | * @returns {Promise} 35 | */ 36 | export const put = (url, data, headers) => request('PUT', url, data, headers); 37 | /** 38 | * 发起delete请求 39 | * @param url 请求路径 必填 40 | * @param data 请求参数 delete请求的参数会自动拼到地址后面 41 | * @param headers 请求头 选填 42 | * @returns {Promise} 43 | */ 44 | export const del = (url, data, headers) => request('DELETE', url, data, headers); 45 | 46 | /** 47 | * 接口请求基类方法 48 | * @param method 请求方法 必填 49 | * @param url 请求路径 必填 50 | * @param data 请求参数 51 | * @param header 请求头 选填 52 | * @returns {Promise} 53 | */ 54 | export function request(method, url, data, header = {'Content-Type': 'application/json'}) { 55 | console.group('==============>新请求<=============='); 56 | console.info(method, url); 57 | if(data) console.info('参数:',data); 58 | return new Promise((resolve, reject) => { 59 | const response = {}; 60 | wx.request({ 61 | url, method, data, header, 62 | success: (res) => response.success = res.data, 63 | fail: (error) => response.fail = error, 64 | complete() { 65 | if (response.success) { 66 | console.info('请求成功:', response.success); 67 | resolve(response.success) 68 | } else { 69 | console.info('请求失败:', response.fail); 70 | reject(response.fail) 71 | } 72 | console.groupEnd(); 73 | }, 74 | }); 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /util/service.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 此文件管理项目所有接口 3 | */ 4 | import {get, post, put, del} from './network'; 5 | 6 | /** 7 | * 服务器根域名 8 | * 试玩更多接口看这里 9 | * http://jsonplaceholder.typicode.com/ 10 | * @type {string} 11 | */ 12 | export const API_ROOT = 'https://jsonplaceholder.typicode.com'; 13 | 14 | 15 | /** 16 | * 获取图片 17 | */ 18 | export const getPhoto = (id) => get(`${API_ROOT}/photos/${id}`); 19 | 20 | --------------------------------------------------------------------------------