uni-app 使用uni.request封装发送api请求文档服务器请求导航守卫

前言

  • 刚刚接触uni-app时候想着直接使用axios发请求,可以发送成功但是请求头有点问题

  • 后面发现教程都是使用@escookrequest-miniprogram三方包发送请求-是没有问题

  • 为什么不直接用uni.request()发送请求,是因为每次请求都要写一次添加请求头不合理

  • 后面随着深入了解uni-app,发现开源框架,若依,芋道,都是封装一层uni.request发请求

  • 解决了统一添加请求头问题,使用uni.request比较合理,自己写可能不全面,可以直接搬

细节

1.观察我们会发现在utils工具文件夹下有upload.js和request.js2个文件在发请求是都在引入

2.request.js基于uni.request封装添加请求头,用户id。专门用来发送普通api请求

3.upload.js基于 uni.uploadFile封装添加请求头,用户id,专门用来对接文档服务器,上传图片文件

4.并不是所有接口都需要添加请求头,在不需要添加的请求头的接口写isToken: false,

代码实现

1.在utils工具文件夹下分别建立request.js和upload.js文件夹-代码如下

request.js

// 引入vuex-调用退出登录方法
import store from '@/store'
// 引入基地址文件
import config from '@/config'
// 引入获取token文件
import { getAccessToken } from '@/utils/auth'
// 引入转台码错误文件
import errorCode from '@/utils/errorCode'
// 引入公共方法文件
import { toast, showConfirm, tansParams } from '@/utils/common'
​
let timeout = 10000
const baseUrl = config.baseUrl;
​
const request = config => {
  // 是否需要设置 token
  const isToken = (config.headers || {}).isToken === false
  config.header = config.header || {}
  if (getAccessToken() && !isToken) {
    config.header['Authorization'] = 'Bearer ' + getAccessToken()
  }
  // 设置租户 TODO 芋艿:强制 1 先
  config.header['tenant-id'] = '1';
  // get请求映射params参数
  if (config.params) {
    let url = config.url + '?' + tansParams(config.params)
    url = url.slice(0, -1)
    config.url = url
  }
  return new Promise((resolve, reject) => {
    uni.request({
        method: config.method || 'get',
        timeout: config.timeout ||  timeout,
        url: config.baseUrl || baseUrl + config.url,
        data: config.data,
        // header: config.header,
        header: config.header,
        dataType: 'json'
      }).then(response => {
        let [error, res] = response
        if (error) {
          toast('后端接口连接异常')
          reject('后端接口连接异常')
          return
        }
        const code = res.data.code || 200
        const msg = errorCode[code] || res.data.msg || errorCode['default']
        if (code === 401) {
          showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
            if (res.confirm) {
              store.dispatch('LogOut').then(res => {
                uni.reLaunch({ url: '/pages/login' })
              })
            }
          })
          reject('无效的会话,或者会话已过期,请重新登录。')
        } else if (code === 500) {
          toast(msg)
          reject('500')
        } else if (code !== 200) {
          toast(msg)
          reject(code)
        }
        resolve(res.data)
      })
      .catch(error => {
        let { message } = error
        if (message === 'Network Error') {
          message = '后端接口连接异常'
        } else if (message.includes('timeout')) {
          message = '系统接口请求超时'
        } else if (message.includes('Request failed with status code')) {
          message = '系统接口' + message.substr(message.length - 3) + '异常'
        }
        toast(message)
        reject(error)
      })
  })
}
​
export default request

upload.js

// 引入vuex-调用退出登录方法
import store from '@/store'
// 引入基地址文件
import config from '@/config'
// 引入获取token文件
import { getAccessToken } from '@/utils/auth'
// 引入转台码错误文件
import errorCode from '@/utils/errorCode'
// 引入公共方法文件
import { toast, showConfirm, tansParams } from '@/utils/common'
​
let timeout = 10000
const baseUrl = config.baseUrl
​
const upload = config => {
  // 是否需要设置 token
  const isToken = (config.headers || {}).isToken === false
  config.header = config.header || {}
  if (getAccessToken() && !isToken) {
    config.header['Authorization'] = 'Bearer ' + getAccessToken()
  }
  // get请求映射params参数
  if (config.params) {
    let url = config.url + '?' + tansParams(config.params)
    url = url.slice(0, -1)
    config.url = url
  }
  // 设置租户 TODO 芋艿:强制 1 先
  config.header['tenant-id'] = '1';
  return new Promise((resolve, reject) => {
      uni.uploadFile({
        timeout: config.timeout || timeout,
        url: baseUrl + config.url,
        filePath: config.filePath,
        name: config.name || 'file',
        header: config.header,
        formData: config.formData,
        method: config.method || 'post',
        success: (res) => {
          let result = JSON.parse(res.data)
          const code = result.code || 200
          const msg = errorCode[code] || result.msg || errorCode['default']
          if (code === 200) {
            resolve(result)
          } else if (code == 401) {
            showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then(res => {
              if (res.confirm) {
                store.dispatch('LogOut').then(res => {
                  uni.reLaunch({ url: '/pages/login/login' })
                })
              }
            })
            reject('无效的会话,或者会话已过期,请重新登录。')
          } else if (code === 500) {
            toast(msg)
            reject('500')
          } else if (code !== 200) {
            toast(msg)
            reject(code)
          }
        },
        fail: (error) => {
          let { message } = error
          if (message == 'Network Error') {
            message = '后端接口连接异常'
          } else if (message.includes('timeout')) {
            message = '系统接口请求超时'
          } else if (message.includes('Request failed with status code')) {
            message = '系统接口' + message.substr(message.length - 3) + '异常'
          }
          toast(message)
          reject(error)
        }
      })
  })
}
​
export default upload

2.在最外层与utils.js同级下创建基地址文件config.js

// 应用全局配置
module.exports = {
  baseUrl: '基地址',
  // 应用信息
  appInfo: {
    // 应用名称
    name: "app",
    // 应用版本
    version: "1.0.0",
    // 应用logo
    logo: "/static/logo.png",
    // 官方网站
    site_url: "",
    // 政策协议
    agreements: [{
        title: "隐私政策",
        url: ""
      },
      {
        title: "用户服务协议",
        url: ""
      }
    ]
  }
}

3.在utils.js工具文件下创建auth.js文件-存储token

RefreshTokenKey-是用来补救token失效换取token的一种无感刷新形式没有不用管

const AccessTokenKey = 'ACCESS_TOKEN'
const RefreshTokenKey = 'REFRESH_TOKEN'
​
// ========== Token 相关 ==========
​
export function getAccessToken() {
  return uni.getStorageSync(AccessTokenKey)
}
​
export function getRefreshToken() {
  return uni.getStorageSync(RefreshTokenKey)
}
​
export function setToken(token) {
  uni.setStorageSync(AccessTokenKey, token.accessToken)
  uni.setStorageSync(RefreshTokenKey, token.refreshToken)
}
​
export function removeToken() {
  uni.removeStorageSync(AccessTokenKey)
  uni.removeStorageSync(RefreshTokenKey)
}

4.在utils.js工具文件下创建errorCode.js文件-状态码错误文件

export default {
  '401': '认证失败,无法访问系统资源',
  '403': '当前操作没有权限',
  '404': '访问资源不存在',
  'default': '系统未知错误,请反馈给管理员'
}

5.在utils.js工具文件下创建common.js文件-公共方法

/**
* 显示消息提示框
* @param content 提示的标题
*/
export function toast(content) {
  uni.showToast({
    icon: 'none',
    title: content
  })
}
​
/**
* 显示模态弹窗
* @param content 提示的标题
*/
export function showConfirm(content) {
  return new Promise((resolve, reject) => {
    uni.showModal({
      title: '提示',
      content: content,
      cancelText: '取消',
      confirmText: '确定',
      success: function(res) {
        resolve(res)
      }
    })
  })
}
​
/**
* 参数处理
* @param params 参数
*/
export function tansParams(params) {
  let result = ''
  for (const propName of Object.keys(params)) {
    const value = params[propName]
    var part = encodeURIComponent(propName) + "="
    if (value !== null && value !== "" && typeof (value) !== "undefined") {
      if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
            let params = propName + '[' + key + ']'
            var subPart = encodeURIComponent(params) + "="
            result += subPart + encodeURIComponent(value[key]) + "&"
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&"
      }
    }
  }
  return result
}

5.在utils.js工具文件下创建permission.js文件-导航守卫设置白名单,页面拦截器

import { getAccessToken } from '@/utils/auth'
​
// 登录页面
const loginPage = "/pages/login"
​
// 页面白名单
const whiteList = [
  '/pages/login', '/pages/common/webview/index'
]
​
// 检查地址白名单
function checkWhite(url) {
  const path = url.split('?')[0]
  return whiteList.indexOf(path) !== -1
}
​
// 页面跳转验证拦截器
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]
list.forEach(item => {
  uni.addInterceptor(item, {
    invoke(to) {
      if (getAccessToken()) {
        if (to.path === loginPage) {
          uni.reLaunch({ url: "/" })
        }
        return true
      } else {
        if (checkWhite(to.url)) {
          return true
        }
        uni.reLaunch({ url: loginPage })
        return false
      }
    },
    fail(err) {
      console.log(err)
    }
  })
})

6.在main.js中引入导航守卫文件

// permission
// 导航守卫文件
import './permission' 

7.发送请求例子

// 对接文档服务器封装
import upload from '@/utils/upload'
// 对接普通api接口
import request from '@/utils/request'
​
// 登录方法-不需要添加token-请求头添加isToken: false
export function login(username, password, captchaVerification) {
    const data = {
        username,
        password,
        captchaVerification
    }
    return request({
        url: '/system/auth/login',
        headers: {
            isToken: false
        },
        'method': 'POST',
        'data': data
    })
}
​
// 查询用户个人信息-普通api添加token+用户id
export function getUserProfile() {
  return request({
    url: '/system/user/profile/get',
    method: 'GET'
  })
}
​
// 用户头像上传-对接文档服务器
export function uploadAvatar(data) {
  return upload({
    url: '/system/user/profile/update-avatar',
    method: 'PUT',
    name: data.name,
    filePath: data.filePath
  })
}

总结:

经过这一趟流程下来相信你也对 uni-app 使用uni.request封装发送api请求文档服务器请求导航守卫 有了初步的深刻印象,但在实际开发中我 们遇到的情况肯定是不一样的,所以我们要理解它的原理,万变不离其宗。加油,打工人!

什么不足的地方请大家指出谢谢 -- 風过无痕

猜你喜欢

转载自blog.csdn.net/weixin_53579656/article/details/131905315