uni-app开发-网络请求封装、拦截器

727 阅读4分钟

一、uni-ui网络请求封装(通过uni.request({})发起网络请求)

uniapp.dcloud.net.cn/api/request…

1. utils工具文件夹中创建一个request.js文件,用于封装网络请求

request.js文件

/**
 * 封装网络请求
 *   {
 *       url:''
 *       method:'get',
 *       data:,
 *
 *   }
 */

export const baseUrl = 'http://localhost:8089'

export const axiosInstance = options => {
    return new Promise((resolve, reject) => {
        uni.request({
            url: baseUrl + options.url,
            method: options.method,
            data: options.data,
            
            success: res => {
                resolve(res.data)
            },

            fail:error=>{
               reject(error)
            }
        })
    })
}
2. utils同级目录创建一个api文件夹(集中管理接口),api文件夹下创建js文件,用于封装项目中所有用到的请求接口

js文件

import { axiosInstance } from '../utils/request.js'

/**
 * 商品列表
 */
export const RequestGoodsList = (pageNo,pageSize=10)=>{
    return axiosInstance({
      url:'/api/shop',
      method:'get',
      data:{
        pageNo,
        pageSize
      }
    })
}

/**
 * 轮播
 */
export const RequestBanner = ()=>{
  return axiosInstance({
  url:'/api/banner',
      method:'get'
  })
}

/**
 * 商品详情
 */
export const RequesetGoodsDetail = (id)=>{
  return axiosInstance({
    url:'/api/shop/find',
    method:'get',
    data:{
      id
    }
  })
}
3. 页面中使用,页面vue文件中引入接口,调用即可
<template>
 <view class="g-container">
   详情页
   <view class="g-goods-wrapper">
     <image :src="goods?.picture" ></image>
     <view class="">{{goods?.product}}</view>
   </view>
 </view>
</template>

<!--
   第一次进入组件时, goods为null,调用其属性报错
 -->

<script setup>
import { ref } from "vue"
import { onLoad } from '@dcloudio/uni-app'
import { RequesetGoodsDetail } from '../../api/goods.js'

// let goods = ref()
let goods = ref({picture:'',product:''})

onLoad((options)=>{
  getDetail()
})

/**
 * 商品详情
 **/

 const getDetail = async()=>{
   const data = await RequesetGoodsDetail(45)
   const { resultCode,resultInfo} = data
   if(resultCode === 1){
     goods.value = resultInfo
     console.log('resultInfo ',resultInfo);
   }
 }

</script>

<style lang="scss"></style>

二、uni-ui拦截器

uniapp.dcloud.net.cn/api/interce…

uni.addInterceptor(STRING, OBJECT)添加拦截器

uni.removeInterceptor(STRING)删除拦截器

参数说明:

STRING:需要拦截的api名称,如:uni.addInterceptor('request', OBJECT) ,将拦截 uni.request()

OBJECT:

参数名类型必填说明
invokeFunction拦截前触发
returnValueFunction方法调用后触发,处理返回值
successFunction成功回调拦截
failFunction失败回调拦截
completeFunction完成回调拦截
1.拦截路由跳转,实现统一登录认证(方法一)
1>. 在utlis文件夹中新建一个interceptor.js文件,用于封装拦截器
/**

 * 统一登录认证, 拦截路由跳转  

 *    重构优化封装

 *   业务 统一登录认证

 *      1. 创建白名单,不需要登录认证的路由直接放行

 *      2. token登录认证

 *      3. 放行

 */

export const routerInterceptor = () => {
  // 1. 路由白名单
  const whileRouters = ['navigateTo', 'navigateBack', 'redirectTo', 'switchTab']
  
  //不需要认证的页面白名单
  const whites = ['/', '/pages/login/login', '/pages/index/index', '/pages/category/category', '/pages/detail/detail']
  
  //遍历路由白名单
  whileRouters.forEach(rouerApi => {
    uni.addInterceptor(rouerApi, {
      invoke(e) {//拦截前触发
        // "url": "/pages/detail/detail?id=1001"
        let url = e.url
        
        if (url.indexOf('?') !== 1) {
          url = url.split('?')[0]
        }
  
        const isOk = whites.some(item => item === url)
  
        if (isOk) {
          return true // 直接放行
        }
  
        // 2. token登录认证
        let token = uni.getStorageSync('TOKEN')
        
        if (token) {
          return true  // 直接放行
        }
  
        console.log('>>>>>11111 不放行 重定向到登录界面 ');
  
        uni.showToast({
          title: '请先登录',
          icon: 'none'
        })
  
        // 3. 不放行 重定向到登录界面
        uni.navigateTo({
          url: '/pages/login/login'
        })
        return false  // 结束不放行

      },

      success(args) {// 成功响应拦截
        console.log('success >>>> ', args);
      },
      
      fail(err) {// 失败响应拦截
        console.log('interceptor-fail', err)
      },
      
      complete(res) {//完成回调拦截
        console.log('interceptor-complete', res)
      }

    })
  })
}
2>. 在App.vue根组件中引入并加载拦截器
<script>
import { routerInterceptor } from './utils/interceptor.js'

export default {
  onLaunch: function() {
    console.log('App Launch')
  
    // 加载拦截器
    routerInterceptor()
  },
  
  onShow: function() {
    console.log('App Show')
  },
  
  onHide: function() {
    console.log('App Hide')
  }
}
</script>

<style>
/*每个页面公共css */
</style>
2.网络拦截器,请求/响应拦截器
1>. login.vue文件中登录保存token,uni.setStorageSync(‘TOKEN’,token)
2>. interceptor.js文件
/**
 * 网络拦截
 */

export const requestInterceptor = () => {
  // 网络请求拦截
  uni.addInterceptor('request', {
    invoke(args) {//拦截前触发
      // 请求拦截器,将token放到请求头里
      const token = uni.getStorageSync('TOKEN')
      
      if(token){
        // args.header['Authorization'] = token
        args.header = {
          Authorization:token
        }
      }
    },
    
    success(args) {// 成功响应拦截
      // 请求成功后,修改code值为1
      args.data.code = 1
    },
    
    fail(err) {
      // 失败响应拦截
      console.log('interceptor-fail', err)
    },
    
    complete(res) {//完成回调拦截
      console.log('interceptor-complete', res)
    }
  })
}
3>. 在App.vue根组件中引入并加载拦截器
<script>
import { requestInterceptor } from './utils/interceptor.js'

export default {
  onLaunch: function() {
    console.log('App Launch')
    // 加载拦截器
    requestInterceptor()
  },
  
  onShow: function() {
    console.log('App Show')
  },
  
  onHide: function() {
    console.log('App Hide')
  }
}
</script>

<style>
/*每个页面公共css */
</style>

三、uView2.x网络请求与拦截器封装

uviewui.com/js/http.htm…

1.request.js文件中
// 此vm参数为页面的实例,可以通过它引用vuex中的变量
module.exports = (vm) => {
    // 初始化请求配置
    uni.$u.http.setConfig((config) => {
        /* config 为默认全局配置*/
        config.baseURL = 'https://www.example.com'; /* 根域名 */
        return config
    })
	
	// 请求拦截
	uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
	    // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
	    config.data = config.data || {}
	    // 根据custom参数中配置的是否需要token,添加对应的请求头
	    if(config?.custom?.auth) {
		// 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中
		config.header.token = vm.$store.state.userInfo.token
	    }
	    return config 
	}, config => { // 可使用async await 做异步操作
	    return Promise.reject(config)
	})
	
	// 响应拦截
	uni.$u.http.interceptors.response.use((response) => { /* 对响应成功做点什么 可使用async await 做异步操作*/
		const data = response.data
		return data.data === undefined ? {} : data.data
	}, (response) => { 
		// 对响应错误做点什么 (statusCode !== 200)
		return Promise.reject(response)
	})
}
2.api集中管理,api文件夹下创建.js文件
const http = uni.$u.http

// post请求,获取菜单
export const postMenu = (params, config = {}) => http.post('/ebapi/public_api/index', params, config)

// get请求,获取菜单,注意:get请求的配置等,都在第二个参数中
export const getMenu = (data) => http.get('/ebapi/public_api/index', {params:data})
3.引用配置

main.js中引用request.js

import App from './App'
import * as Pinia from 'pinia';

// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
    ...App
})
app.$mount()
// #endif

import uView from "uview-ui";
Vue.use(uView);//引入并使用uView的JS库

// 引入请求封装,将app参数传递到配置中
require('/util/request.js')(app)

// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
  const app = createSSRApp(App)
  app.use(Pinia.createPinia()); // 集成状态管理pinia
  return {
    app,
	Pinia, // 此处必须将 Pinia 返回
  }
}
// #endif
4.页面中使用,页面vue文件中引入接口,调用即可
import { postMenu, getMenu } from '/config/api.js';

// 发出post,假设需要带上token
postMenu({ custom: { auth: true }}).then(() => {}).catch(() =>{})

// await等待,注意与async结合使用
await postMenu({ custom: { auth: true }})

// get请求
getMenu({ custom: { auth: true }}).then(() => {}).catch(() =>{})

// 也可以直接通过uni.$u.post发出请求,注意此处需要写上接口地址
uni.$u.http.post('/common/menu', { custom: { auth: true }}).then(() => {}).catch(() =>{})

四、实现统一登录认证(方法二)

uni-app不自带导航守卫,实现方法有两种:

1.使用拦截器自行封装,具体如上第二条。
2.使用插件。
①安装:

npm install uni-simple-router

npm install uni-read-pages

②在项目根目录新建vue.config.js文件
const TransformPages=require('uni-read-pages')
const {webpack} = new TransformPages()
module.exports ={
  configureWebpack:{
    plugins:[
      new webpack.DefinePlugin({
        ROUTES:webpack.DefinePlugin.runtimeValue(()=>{
          const tfPages = newTransformPages({
            includes:['path','name",'aliasPath']
          });
          return JSON.stringify(tfPages.routes)
        },true )
      })
    ]
  }
}
③项目根目录创建router.js文件
import {RouterMount,createRouter} from 'uni-simple-router';
 
const router = createRouter({
	platform: process.env.VUE_APP_PLATFORM,  
	routes: [
          //进入购物车之前判断登录状态,如果是未登录跳转到首页
          {
            path:"/pages/cart/cart",
            beforeEnter:(to,from,next)=>{
              let status = store?.state?.user?.status;
              if( !status ){
                console.log('跳转到登录页");
                next('/pages/login/login");
              }
              next();
          },
          ...ROUTES
        ]
});
//全局路由前置守卫
router.beforeEach((to, from, next) => {
	next();
});
 
export {
	router,
	RouterMount
}
④在 main.js 引入 router.js
import App from './App'
import * as Pinia from 'pinia';

import {router,RouterMount} from './router.js'  //路径换成自己的
Vue.use(router)

// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
    ...App
})
app.$mount()
// #endif

// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
  const app = createSSRApp(App)
  app.use(Pinia.createPinia()); // 集成状态管理pinia
  return {
    app,
	Pinia, // 此处必须将 Pinia 返回
  }
}
// #endif

//v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
// #ifdef H5
	RouterMount(app,router,'#app')
// #endif
 
// #ifndef H5
	app.$mount(); //为了兼容小程序及app端必须这样写才有效果
// #endif