Request 请求封装思路
发布于 3 年前 作者 xiuying52 4011 次浏览 来自 分享

请求封装前言

  • 通常在小程序项目中,微信已经提供了网络请求API,但是往往这点并不能满足我们,所以有了下面的封装

  • Requset请求往往我们有很多需求,此处简单罗列需求并代码实现

    1. 根据不同版本小程序 develop --> trial --> release 版本使用不同域名
    2. request 拦截器,对当前请求发送前做相应处理
    3. 可控式请求 loading 动画,同时支持扩展自定义 loading 文字提示
    4. 根据后端返回请求响应结果做相应处理(列如 210 未登录,220 登录失效等需要与后端共同定义)
  • 笔者使用的是 Taro 进行请求封装,解释步骤基本在代码中有描述,笔者没有对每个方法拆分详细讲述
  • 笔者是根据之前分享的是Taro模板中的请求封装,拆分讲述思路,可供参考借鉴
  • 前言废话太多下面上代码

开始

  • 首先在项目 pages 平级创建 service 文件夹,然后分别建立3个文件分别是:
    1. interceptors.ts – 请求响应拦截器。
    import Taro from '@tarojs/taro'
    import { HTTP_STATUS } from './status'
    // 笔者这里引入 mobx 是对 未登录,登陆失效 等做处理
    import cartStroe from '../store/user'
    

    const customInterceptor = (chain:any) => {

    <span class="hljs-keyword">const</span> requestParams = chain.requestParams
    
    <span class="hljs-keyword">return</span> chain.proceed(requestParams).then(<span class="hljs-function">(<span class="hljs-params">res:any</span>) =&gt;</span> {
        <span class="hljs-comment">// 清除 loading</span>
        <span class="hljs-keyword">if</span>(requestParams.loading) Taro.hideLoading()
        <span class="hljs-keyword">switch</span>(res.statusCode) {
            <span class="hljs-keyword">case</span> HTTP_STATUS.SUCCESS:
                <span class="hljs-keyword">const</span> result = res.data
                <span class="hljs-keyword">if</span>(res.data.code === <span class="hljs-number">200</span>) {
                    <span class="hljs-comment">// 接口调通且无异常赋予success标识</span>
                    result.success = <span class="hljs-literal">true</span>
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">// 请求接口错误提示,可通过参数中加入 openErrTips: false 关闭</span>
                    <span class="hljs-keyword">if</span>(requestParams.openErrTips &amp;&amp; result.msg) Taro.showToast({ <span class="hljs-attr">title</span>: result.msg, <span class="hljs-attr">icon</span>: <span class="hljs-string">'none'</span> })
                    <span class="hljs-comment">// 登录过期或未登录 需要与后端共同定义</span>
                    <span class="hljs-keyword">if</span>(result.code === <span class="hljs-number">210</span> || result.code === <span class="hljs-number">220</span>) {
                        <span class="hljs-comment">// 跳转登陆 清空用户信息等 处理</span>
                        cartStroe.setStatus(<span class="hljs-literal">false</span>)
                        cartStroe.setUser({})
                        Taro.showToast({ <span class="hljs-attr">title</span>: (result.code === <span class="hljs-number">210</span> ? <span class="hljs-string">'未登录,请先登陆'</span> : <span class="hljs-string">'登录信息失效,请重新登陆'</span> ), <span class="hljs-attr">icon</span>: <span class="hljs-string">'none'</span> })
                        Taro.navigateTo({ <span class="hljs-attr">url</span>: <span class="hljs-string">'/pages/login/index'</span> })
                        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(result)
                    }
                }
                <span class="hljs-keyword">return</span> result
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.CREATED:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'请求成功并且服务器创建了新的资源'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.ACCEPTED:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'接受请求但没创建资源'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.CLIENT_ERROR:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'服务器不理解请求的语法'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.AUTHENTICATE:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.FORBIDDEN:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'服务器拒绝请求'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.NOT_FOUND:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'服务器找不到请求的网页'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.SERVER_ERROR:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'(服务器内部错误) 服务器遇到错误,无法完成请求'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.BAD_GATEWAY:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.SERVICE_UNAVAILABLE:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'(服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。'</span>)
    
            <span class="hljs-keyword">case</span> HTTP_STATUS.GATEWAY_TIMEOUT:
                <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.reject(<span class="hljs-string">'(网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求'</span>)
    
            <span class="hljs-attr">default</span>:
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'请开发者检查请求拦截未匹配到错误,返回statusCode :&gt;&gt; '</span>, res.statusCode)
                <span class="hljs-keyword">break</span>
            
        }
    })
    

    }

    // Taro 提供了两个内置拦截器 // logInterceptor - 用于打印请求的相关信息 // timeoutInterceptor - 在请求超时时抛出错误。 const interceptors = [customInterceptor, Taro.interceptors.logInterceptor]

    export default interceptors

    1. status.ts – HTTP 通用响应状态码提示(方便开发者寻找请求错误源)。
    export const HTTP_STATUS = {
        // 成功处理了请求,一般情况下都是返回此状态码
        SUCCESS: 200,
        // 请求成功并且服务器创建了新的资源
        CREATED: 201,
        // 接受请求但没创建资源
        ACCEPTED: 202,
        // 服务器不理解请求的语法
        CLIENT_ERROR: 400,
        // 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应
        AUTHENTICATE: 401,
        // 服务器拒绝请求
        FORBIDDEN: 403,
        // 服务器找不到请求的网页
        NOT_FOUND: 404,
        // (服务器内部错误) 服务器遇到错误,无法完成请求
        SERVER_ERROR: 500,
        // (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应
        BAD_GATEWAY: 502,
        // (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
        SERVICE_UNAVAILABLE: 503,
        // (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求
        GATEWAY_TIMEOUT: 504
    }
    

    /**

    • 此处有替补实现方式
    • 使用键值对直接通过状态码取值
    • const HTTP_STATUS = {
    • ‘200’: ‘请求服务器端成功’,
    • ‘201’: ‘’
    • }
    • if(HTTP_STATUS[res.statusCode])
    • console.log(HTTP_STATUS[res.statusCode])
    • else
    • console.log(请开发者检查请求拦截未匹配到错误,返回statusCode :&gt;&gt; ${res.statusCode}) */
  1. index.ts – 封装请求导出供调用。
import Taro from '@tarojs/taro'

import interceptors from ‘./interceptors’

interceptors.forEach(interceptorItem => Taro.addInterceptor(interceptorItem)) // 模块,命名空间,基础接口声明,此处不作解释 自行了解,结尾提供文档指引 declare namespace RequestProps { interface Method { ‘GET’, ‘POST’, ‘PUT’, ‘DELETE’ } interface Options { url: string, method: keyof Method, data: any, loading?: boolean, loadingTitle?: string, contentType?: string, openErrTips?: boolean } interface requestParams { url: string, method: keyof Method, data: any, header: any, loading?: boolean, loadingTitle?: string, contentType?: string, openErrTips?: boolean } }

/**

  • 获取版本 retrun 对应环境域名

  • develop: ‘开发版’, trial: ‘体验版’, release: ‘正式版’

  • 支持扩展 - 思路 可通过 process.env.NODE_ENV 判断当前打包是 生产模式或工厂模式 进而判断 适合多环境 dev -> beta -> uat -> pro

  • @returns 域名 */ const getVersion = () => { // @ts-ignore switch (__wxConfig.envVersion) { case ‘develop’: return http://develop.gavinpeng.club

    case ‘trial’: return http://trial.gavinpeng.club

    case ‘release’: return http://release.gavinpeng.club

    default: return http://develop.gavinpeng.club } }

class Request { baseOptions(options: RequestProps.Options) { let { url, method, data } = options // 过滤 扩展属性 let { loading, loadingTitle, contentType, openErrTips, …rest } = data

    <span class="hljs-keyword">if</span>(loading) Taro.showLoading({ <span class="hljs-attr">title</span>: loadingTitle || <span class="hljs-string">'加载中...'</span>, <span class="hljs-attr">mask</span>: <span class="hljs-literal">true</span>  })
    <span class="hljs-keyword">const</span> requestParams: RequestProps.requestParams = {
        <span class="hljs-attr">url</span>: getVersion() + url,
        method,
        <span class="hljs-attr">data</span>: rest,
        <span class="hljs-attr">header</span>: {
            <span class="hljs-comment">// 支持自定义 contentType</span>
            <span class="hljs-string">'content-type'</span>: contentType || <span class="hljs-string">'application/json'</span>,
            <span class="hljs-comment">// Token</span>
            <span class="hljs-string">'Authorization'</span>: Taro.getStorageSync(<span class="hljs-string">'token'</span>)
            <span class="hljs-comment">// 此处支持扩展,可通过请求 data 参数中加入 header 对象,在上面过滤 语法糖 ...header 此处就不做过多解释,需要的自行添加了解</span>
            <span class="hljs-comment">// ...header</span>
        },
        <span class="hljs-comment">// 请求是否带 loading, 传递到 请求响应拦截器 清除 loading </span>
        loading,
        openErrTips
    }
    <span class="hljs-keyword">return</span> Taro.request(requestParams)
}

<span class="hljs-keyword">get</span>(url:string, data:any) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.baseOptions({ url, <span class="hljs-attr">method</span>:<span class="hljs-string">'GET'</span>, data })
}

post(url:string, <span class="hljs-attr">data</span>:any) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.baseOptions({ url, <span class="hljs-attr">method</span>:<span class="hljs-string">'POST'</span>, data })
}

}

export default new Request()

  • 使用方式(笔者对接口模块区分接口管理)
    1. 在项目 pages 平级创建 api 文件夹(统一管理接口)
    2. api 建立 index.ts (导出接口)
    import * as test from './test'
    

    const Api = { // 测试模块 …test, // xxx 模块 } // 导出所有接口 export default Api

    1. api 建立测试模块接口列表 test.ts
    /*
    * @Author: Gavin
    * @CreateTime: xxxx
    * @Describe: 测试模块相关接口
    */
    // 引入封装后的请求方法
    import request from '@/service/index'
    

    /**

    • 测试
    • @param params
    • @returns */ export const isTest = (url:string, data:any) => { return request.post(url, data) }
    1. page 页面引入接口 import Api from '@/api/index' 并使用接口
    Api.isTest({
        id: 1,
        name: 'Gavin',
        // 扩展参数 - 是否需要 loading 非必传默认:false
        loading: true,
        // 扩展参数 - 是否自定义 loadingTitle 非必传默认:加载中...(注:没有开启 loading 此参数无效)
        loadingTitle: '自定义加载提示',
        // 扩展参数 - 是否自定义 contentType 非必传默认:application/json
        contentType: 'x-www-form-urlencoded',
        // 扩展参数 - 是否需要请求错误提示 openErrTips 非必传默认:false
        openErrTips: true,
        // 扩展参数 - 上面讲到的 自定义 header 需要的在封装中添加
        // header: { }
    }).then((res:any) => {
        // 成功
    }).catch((err:any) => {
        // 失败
    })
    

    结尾

    • 讲述过程中如有不对或错地方还请指出,欢迎各路大佬们指教
    • 每一次分享是为了进步,在此过程中,讲述代码思路,亦可以很好的锻炼自身的表达能力
    • 成长是生活的动力,加油吧新生代农民工们!
      TypeScript 中文文档指引
      请求封装陈述源码地址
    回到顶部