【小程序-慕尚花坊05】项目中网络请求封装

项目中网络请求封装

如需转载,请附上链接https://blog.csdn.net/m0_59269218/article/details/151030396

一,项目中网络请求封装

本篇文章依旧是慕尚花坊网络封装篇,从前面三篇网络请求是自定义底层逻辑的封装,为了和项目统一,这里使用项目自定义的 mina-request 实现功能,其请求地址如下: https://www.npmjs.com/package/mina-request

在学习本系列之前,再次先提供一下尚硅谷给的接口文档和笔记资料,以及自己代码的仓库地址

接口文档:https://s.apifox.cn/6ed6c5c4-56c4-4619-8e2a-4817aa140e30

笔记资料:https://www.yuque.com/bigweb/dpa5f7/dm2ygniwxksuxy6o

学习视频对应链接:https://www.bilibili.com/video/BV1LF4m1E7kB

本系列个人的gitee仓库地址:https://gitee.com/zhenghuisheng/mu-shanghua-fang

1,npm安装request请求

接下来就是继续安装 mina-request 请求模块,可以通过上面那个官方文档跟着安装就行,安装完成之后,就会出现成功增加一个包的提示

js 复制代码
npm install mina-request

随后点击工具,在点击构建npm,随后出现完成构建npm的标志。在miniprogram_npm文件夹中,就会出现mina-request 这个新文件夹,里面就是刚刚安装的一些东西

随后将request.js文件中的WxRequest引入方式修改一下,将./request改成刚刚安装的mina-request,这样就能保证后面的逻辑能和项目的一致,因为前面三篇自己封装的,难免可能会和老师封装的不太一致

js 复制代码
// import WxRequest from './request'
import WxRequest from 'mina-request'

需要在响应拦截器中对返回的数据进行打印,点击测试请求按钮之后,可以发现相应的数据已经返回

java 复制代码
instance.interceptors.response = async (response) => {
  console.log(response)
}     

2,http.js文件

前面三篇文章已经将http.js文件写了一遍,这里由于是正式项目的网络请求封装,因此再这里再写一遍,这里稍微回会和前面写的http.js文件有点不一样,需要经过小部分的改动,这里只对里面的代码进行拆分与讲解,合起来就是完整代码,整个文件的详细代码可以访问git地址:https://gitee.com/zhenghuisheng/mu-shanghua-fang

2.1,定义默认请求文件

首先第一步就是定义一个默认的实例,再定义前,需要将之前引入的 ./request 替换成mina-request ,然后构建一个默认的 WxRequest 实例,定义url前缀、超时时间以及是否需要isLoading加载,这里默认为true

js 复制代码
// import WxRequest from './request'
import WxRequest from 'mina-request'
import { clearStorage, getStorage } from './storage'
import { modal, toast } from './commonApi'

// 对 WxRequest 进行实例化,不管是这里还是外面都是用上面定义的构造方法
// 只要是对象即可,不会像java一样那么严格
const instance = new WxRequest({
  baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
  timeout: 15000,
  isLoading: true
})

2.2,定义请求拦截器

在调用服务器接口的时候,需要判断一下是否携带token,所以就需要定义一个请求拦截器判断token是否携带,需要从storage本地缓存中获取,存的话一般是在登录后服务器把token返回给前端存储,再请求拦截器中只需要取token就好,将取到的token存到header请求头中

js 复制代码
// 定义默认的请求拦截器
instance.interceptors.request = (config) => {
  // 从本地获取 token
  const token = getStorage('token')
  if (token) {
    // 如果存在 token ,则添加请求头
    config.header['token'] = token
  }
  return config
}

2.3,定义相应拦截器

定义相应拦截器稍微回比请求拦截器复杂一些,需要通过服务器返回的数据进行对应的业务逻辑判断:

  • 首先是对 isSuccess 这个布尔类型的数据进行判断,只要请求能访问到服务器,isSuccess就会返回true,如果返回的isSuccess是false,那么就说明请求没到服务器,网络出现异常
  • 其次是对服务器返回的code进行业务逻辑判断,如果code返回的是200,那么表示请求返回成功
  • 如果code返回的是208,那么表示此次请求用户没传token或者token已经失效,并且清除缓存跳转到登录页
  • 如果code既不是200也不是208,那么抛出一个默认异常

注意:这里的code均为该项目后端自定义的code,不要和其他项目混合

js 复制代码
// 定义默认的响应拦截器
instance.interceptors.response = async (response) => {
  console.log(response)
  const { isSuccess, data } = response
  // 出现网络异常,正常来说只要请求能访问到服务器,isSuccess就会返回true
  // 不管后端的code是多少,如果返回的isSuccess是false,那么就说明请求没到服务器,网络出现异常
  if (!isSuccess) {
    toast({ title: '网络错误', icon: 'error' });
    // 抛出异常
    return Promise.reject(response);
  }
  // 如果isSuccess返回true,那么就需要对后端返回的code进行判断及做相应的业务
  switch (data.code) {
    // code 等于 200,后端返回的是请求成功的数据
    case 200: return data
    // 如果是208,那么说明没有token或者token失效
    case 208:
      const res = await modal({
        content: '鉴权失败,请重新登录',
        showCancel: false
      })
      if (res) {
        // 清除用户信息,如token
        clearStorage()
        // 跳转到登陆页面
        wx.navigateTo({
          url: '/pages/login/login'
        })
      }
      return Promise.reject(response)
    // 其他code做统一的异常封装  
    default:
      toast({
        title: '程序异常,请联系客服或稍后重试!'
      })
      // 抛出异常
      return Promise.reject(response)
  }
}

完成前面三个操作之后,最后将实例暴露出去,那么外部就能调用

js 复制代码
// 将 WxRequest 的实例通过模块化的方式暴露出去
export default instance

2.4,请求调用测试

在test.wxml文件中,定义一个按钮,并且绑定一个事件handler

js 复制代码
<view><button bindtap="handler">测试请求</button></view>

在test.js文件中,定义这个handler自定义事件,并且发起请求,请求后将异常捕获,如测试这个正确的请求

java 复制代码
import instance from '../../utils/http'
Page({
  // 点击按钮触发 handler 方法
  async handler() {
    // 请求并且进行异常捕获
    const res = await instance.get(instance.get("/index/findBanner").catch((error) => {
      console.log(error)
      return error
    }))
    console.log(res)
  }
})

点击按钮之后,其效果如下,会返回相应的数据,isSuccess为true,code为200。

3,设置环境变量

在实际开发中,不同的开发环境,调用的接口地址是不一样的。例如开发环境需要调用开发版的接口地址,生产环境需要调用正式版的接口地址,这时候,我们就可以使用小程序提供了 wx.getAccountInfoSync() 接口,用来获取当前账号信息,在账号信息中包含着 小程序 当前环境版本。

微信小程序客户端的官网地址如下:https://developers.weixin.qq.com/miniprogram/dev/api/

接下来在app.js文件中对这个接口进行测试,看一下可以获取到哪些信息

js 复制代码
App({
  onShow(){
    const countInfo = wx.getAccountInfoSync()
    console.log(countInfo)
  }
})

其效果如下,打印了appId以及envVersion,envVersion 就是这里需要的环境变量

接下来在utils文件下新建一个env.js文件,在小程序开发中一般会有三个环境:开发版,体验版,正式版

环境版本 合法值
开发版 develop
体验版 trial
正式版 release
  • 开发版一般指的是开发人员正在开发的版本,并且没在小程序后台管理变成体验版
  • 体验版需要管理人员将开发版手动设置成体验版,有体验权限的人才能看到
  • 正式版需要管理人员将体验版手动设置成正式版,需要通过腾讯那边审核,审核通过就能被用户访问

一般不同环境访问服务器的域名端口等都不太一样,比如需要连接后端的本地环境或者测试环境等,因此需要通过不同的环境设置不同的域名,获取域名就可以直接导入引用这个js文件即可

js 复制代码
// 获取 小程序帐号信息
const { miniProgram } = wx.getAccountInfoSync()
// 获取小程序当前开发环境
// develop 开发版, trial 体验版, release 正式版
const { envVersion } = miniProgram
let env = {
  baseURL: 'https://gmall-prod.atguigu.cn/mall-api'
}
switch (envVersion) {
  case 'develop': env.baseURL = 'https://gmall-prod.atguigu.cn/mall-api' break
  case 'trial': env.baseURL = 'https://gmall-prod.atguigu.cn/mall-api' break
  case 'release': env.baseURL = 'https://gmall-prod.atguigu.cn/mall-api' break
  default: env.baseURL = 'https://gmall-prod.atguigu.cn/mall-api' break
}
export { env }

4,调用接口方式说明

为了更好统一管理所有的api请求,那么就需要在 miniprogram 目录下新建一个api文件夹,后续所有的请求都会放到这个api文件目录下,比如index页面,那么就在api文件夹下面新建一个index.js文件,index.js文件内容如下

js 复制代码
// 导入http模块
import http from '../utils/http'
// 定义请求,比如一些关于index页面的增删改查都在这个文件下面定义
export const findBanner = () => http.get('/index/findBanner')

再回到index页面的目录下,其index.js文件内容如下,需要引入api文件夹下的index.js文件,并且引入刚刚定义的获取banner的请求,然后在onLoad生命周期调用这个findBanner方法即可

js 复制代码
// 导入接口 API
import { findBanner } from '../../api/index'
Page({
  // 小程序页面加载时执行
  onLoad () {
    // 调用获取首页数据的方法
    this.getHomeList() // 使用 this 来调用页面内的方法
  },
  // 获取首页数据
  async getHomeList() {
    try {
      // 获取轮播图数据
      const res = await findBanner()
      console.log('轮播图数据为' + JSON.stringify(res))
    } catch (error) {
      console.error('获取首页数据失败:', error) // 错误处理
    }
  }
})

在普通编译之后,其打印的数据如下,可以发现这个轮播图数据成功打印出来

到此为止,整个网络请求相关的封装到此结束,后续调用后端的接口直接通过上面这种方式即可,在api文件中定义相关请求,在页面中的业务.js文件中引入api请求即可,让页面中的js文件更加的关注自己的业务实现即可,同时通过层层封装的方式实现高内聚、低耦合