zoukankan      html  css  js  c++  java
  • 接口封装

    前端的动态数据交互离不开服务端提供的接口,在一个前后端分离的中后台项目中,接口的请求和响应是必不可少的。

    那么在架构一个中后台系统的时候,我们如何有效的管理和封装接口,提高项目接口调用的统一性、可维护性,以及在后端接口还没有开发完成,在仅有契约的基础上我们如何有效的模拟接口的调用呢?

    接下来便会对以上问题提供个人解决方案供大家参考。

    1. 不封装存在的问题

    首先谈谈接口封装,因为我们使用的请求库是 axios,所以接下来的示例都以 axios 来举例。

    那么在没有封装接口的项目中,你可能随处可见接口的直接调用方法,比如像这样:

    axios.post('/user', {
        firstName: 'zhang',
        lastName: 'san'
      })
      .then(function (response) {
        console.log(response);
      });
    ...
    
    axios.get('/user?ID=12345')
        .then(function (response) {
        // handle success
        console.log(response);
      });
    复制代码

    这样的写法会存在一些缺点,主要有以下几点:

    • 接口 url 没有统一管理,散落在项目的各个地方
    • 如果需要在接口调用成功和失败时做一些处理,需要在每个地方进行添加
    • 特殊请求头以及取消请求方法需要单独进行编写

    2. 修改默认配置

    既然会存在上述问题,那么我们就需要去解决。在之前介绍的项目目录结构中,我们会发现有 services 文件夹,这就是用来存放封装的接口和调用的方法的。

    在接口封装过程中,首先我们需要修改 axios 的默认配置,如下:

    import axios from 'axios'
    
    // 修改默认配置
    axios.defaults.headers.post['Content-Type'] = 'application/json'
    axios.defaults.headers.get['Content-Type'] = 'application/json'
    axios.defaults.withCredentials = true // 表示是否跨域访问请求
    复制代码

    可以把你常用的请求头的 Content-Type 设置为默认值,同时开启跨域功能。

    3. 设置拦截器

    接下来需要编写下请求和响应的拦截器,来对请求和响应进行适时拦截,比如再调用重复接口时,取消上一次未完成的相同请求:

    const CancelToken = axios.CancelToken
    const httpPending = [] // 用于存储每个ajax请求的取消函数和ajax标识
    
    // 取消请求方法
    const cancelHttp = (name, config = {}) => {
        httpPending.forEach((e, i) => {
            if (e.n === name || e.n === config.xhrName) { // 当前请求在数组中存在时执行函数体
                e.f() // 执行取消操作
                httpPending.splice(i, 1) // 把这条记录从数组中移除
            }
        })
    }
    
    // 请求拦截器
    axios.interceptors.request.use(config => {
        // 取消上一次未完成的相同请求,注意项目中是否存在风险
        cancelHttp(null, config)
    
        config.cancelToken = new CancelToken(c => {
            if (config.xhrName) {
                httpPending.push({
                    n: config.xhrName,
                    u: `${config.url}&${config.method}`,
                    f: c
                })
            }
        })
    
        return config
    }, error => Promise.reject(error))
    
    // 响应拦截器
    axios.interceptors.response.use(res => {
        cancelHttp(null, res.config) // 响应成功把已经完成的请求从 httpPending 中移除
    
        checkStatus(res) // 校验响应状态
        const response = res.data
    
        return Promise.resolve(response)
    }, error => Promise.reject(error))
    复制代码

    上述两个拦截器主要做了重复请求的拦截功能,在请求头中将请求的取消请求方法和标识符号插入数组中,当然之前需要去数组中查找是否存在相同请求,存在则提前取消请求,最后在响应时把已经完成的请求信息从数组中移除。

    这里为了避免风险,我们需要在接口调用的地方手动传入一个 xhrName 标识这个请求名称才会取消重复调用该接口的请求,其余接口不做处理。

    同时我们也将 cancelHttp 暴露给全局,满足手动取消请求的需要:

    Vue.prototype.$cancelHttp = cancelHttp
    复制代码

    4. 暴露调用方法

    当我们完成了针对 axios 的一些设置后,我们最终的目的是使用它来请求和处理接口,那么是时候对请求调用的方法进行封装和暴露了:

    import uriConfig from '@/config/apiUriConf'
    import GLOBAL from '@/config/global' // 全局变量
    
    ...
    
    export default class Http {
        static async request(method, url, opts, type) {
            // 开启本地 mock 的话,不使用接口域名
            let hostName = GLOBAL.mockLocal ? '' : uriConfig.apiUrl
            
            // 特殊接口域名
            let otherName = GLOBAL.mockLocal ? '' : (uriConfig[type] || '')
            
            // type 存在则使用对应的接口,否则使用通用接口
            let uri = type ? `${otherName}${url}` : `${hostName}${url}`
            
            // 接口别名、请求方式及url
            let params = {
                xhrName: (opts && opts.name) || '',
                method,
                url: uri,
            }
            
            // 请求数据
            params.data = opts.body || {}
            
            // 设置特殊请求头
            if (opts.type === 'formData') {
                params.headers = {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }
    
            return axios(params)
        }
    
        static get(url, opts) {
            return this.request('GET', url, opts)
        }
    
        static put(url, opts) {
            return this.request('PUT', url, opts)
        }
    
        static post(url, opts) {
            return this.request('POST', url, opts)
        }
    
        static patch(url, opts) {
            return this.request('PATCH', url, opts)
        }
    
        static delete(url, opts) {
            return this.request('DELETE', url, opts)
        }
    }
    复制代码

    上方我们将封装了一个 Http 类,其中包含了 get、post 等请求方法,这些请求方法内部都会去调用 request 方法,该方法会通过传入的不同参数执行原始 axios 的请求调用,返回一个 Promise。

    5. 引用调用方法

    那么哪里去使用这个 Http 类呢,我们可以在 services 文件夹中再建立其他接口管理文件,比如 user.js,用于存放用户相关的接口:

    // user.js
    import Http from './http'
    
    // 获取用户信息
    export const getUserInfo = params => Http.post('/getUserInfo', {
        body: params
    })
    
    复制代码

    最后在调用的地方引入 getUserInfo 方法,传入对应的参数对象即可。

    import { getUserInfo } from '@services/user'
    
    getUserInfo({
        token: 'xxx'
    })
    复制代码

    如此我们便完成了接口封装的基本功能

    接口模拟

    刚刚在封装接口的时候,我们看到了 GLOBAL.mockLocal 这一全局变量,用于判断是否开启和关闭接口的 mock。

    首先什么是接口 mock?意思就是模仿接口返回的数据。那么为什么要模仿呢?因为在接口还没开发完的场景下,前端在得知接口文档和格式后想本地模拟数据请求的功能,这时候开启接口 mock 就会变得十分简单。

    这里我们通常会用一个比较实用的库 mockjs 来实现我们的功能,用法很简单,在我们事先创建的 mock 文件夹下创建 index.js 文件:

    import GLOBAL from '@/config/global'
    
    // 全局变量
    if (GLOBAL.mockLocal) {
        let Mock = require('mockjs')
    
        // 用户信息接口
        Mock.mock('/getUserInfo', () => ({
            result: {
                name: '张三',
                sex: 'man',
                age: 12,
            },
            status: true, // 数据状态
            statusCode: '200', // 状态码
            message: '请求成功' // 提示信息
        })
    }
    
    复制代码

    文件中我们还是以用户信息接口为例,当开启全局 mock 的时候,我们便使用 mockjs 来模拟数据的返回。

    当然你还需要的项目的入口文件中引用下:

    // main.js
    import '@/mock' // 全局变量
    
    复制代码

    之后配合接口封装时判断开启 mock 就不使用接口域名的功能,我们在正常调用接口的时候便能直接获取自己模拟的数据结果。

    sunshine15666
  • 相关阅读:
    【Python-虫师】自动化测试模型--参数化
    【Loadrunner】【浙江移动项目手写代码】代码备份
    虫师的性能测试思想html网页学习
    Loadrunner之https协议录制回放报错如何解决?(九)
    【Python虫师】多窗口定位
    【虫师讲Selenium+Python】第三讲:操作测试对象
    【虫师Python】第二讲:元素定位
    【小甲鱼】【Python】正则表达式(三)
    【小甲鱼】【Python】正则表达式(二)
    js提交数据时需判断是点击事件还是回车键
  • 原文地址:https://www.cnblogs.com/xiaolucky/p/11548630.html
Copyright © 2011-2022 走看看