前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >子应用共享http请求对象

子应用共享http请求对象

作者头像
copy_left
发布2020-11-26 12:06:20
4970
发布2020-11-26 12:06:20
举报
文章被收录于专栏:方球方球

背景

项目为旧单体项目, 改造为微前端项目。所以原项目存在大量共享工具及组件。当前方案是将这些共享代码根据功能,拆分为第三方包。这其中就包括http请求对象。而后端API并不参入前端业务的拆分,所以我们需要保证子应用与基座使用相同的请求配置。 这里记录相关的解决思路。

目标

我们希望子应用存在独立开发和嵌入基座的能力。

  • 独立开发: 使用本地请求对象
  • 嵌入基座:使用基座请求对象

目录

  • pkgs
    • http 公共请求封装
    • api 可公用的特定请求函数

这里我们将分为两个独立的包, http负责最基础的业务请求对象封装,例如登录拦截, token设置,接口兼容等, api 负责具体的业务请求,提供公共的请求方法。例如: 用户信息查询, 状态查询等.

http 包

目录

  • http
    • src
      • core.ts 请求封装
      • transform-res.ts 响应拦截
      • transform-req.ts 请求拦截
      • utils.ts 工具函数
      • index.ts 入口

核心

代码语言:javascript
复制
import { AxiosInstance, AxiosRequestConfig }  from 'axios'
import qs from 'query-string'
import { deepCopy, getParams } from './utils'
const DEFAULT_CONF:AxiosRequestConfig = {
  timeout: 1000 * 6,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json; charset=utf-8'
  },
  baseURL: process.env.NODE ? '/DEV_PROXYY' : '',
  paramsSerializer
}


export interface iTimetamp{
  T:number
  [props:string]:any
}


/**
 * 刷新请求
 * @summary 通过添加多余的时间戳参数,防止浏览器缓存,刷新请求。
 */
export function paramsSerializer<T extends iTimetamp>(params:T):string{
  params = params.T ? { ...params, T: new Date().getTime() } : params
  return qs.stringify(params)
}


/**
 * 默认配置
 */
export function getDefaultConf():AxiosRequestConfig{
  return deepCopy<AxiosRequestConfig>(DEFAULT_CONF)
}


/**
 * 请求对象
 * @summary 
 * 使用单例模式,全局提供唯一请求对象
 * 封装常用请求方式
 * @props
 *  - _axios axios实例
 *  - CONF 当前全局配置
 * @maths
 *  - GET
 *  - POST
 *  - PUT
 *  
 *  - addResInterceptors response 拦截器
 *  - addReqInterceptors request 拦截器
 */
export class MicroHttp {
  static _http:MicroHttp
  _axios:AxiosInstance = {} as AxiosInstance // 单例兼容写法
  interceptors:AxiosInstance['interceptors'] = {} as AxiosInstance['interceptors']
  
  constructor(axiosObj:AxiosInstance){


    if(MicroHttp._http){
      return MicroHttp._http
    }
    
    this._axios = axiosObj
    this.interceptors = this._axios.interceptors
    MicroHttp._http = this
    
  }


  /**
   * 二次封装请求接口
   *  因为会使用中间件对返回数据做解包
   * 所以方法返回的类型直接使用了 【泛型T】而不是原方法默认的 【AxiosResponse<T>】
   */
  GET<Q, T>(url:string|[string, Q], conf?:AxiosRequestConfig):Promise<T>{
    const [_url, _conf] = getParams(url, conf)
    return MicroHttp._http._axios.get<T, T>(_url, _conf)
  }


  POST<Q, T>(url:string|[string, Q], data?:any, conf?:AxiosRequestConfig):Promise<T>{
    [url, conf] = getParams(url, conf)
    return MicroHttp._http._axios.post<T, T>(url, data, conf)
  }


  PUT<Q, T>(url:string|[string, Q], data?:any, conf?:AxiosRequestConfig):Promise<T>{
    [url, conf] = getParams(url, conf)
    return MicroHttp._http._axios.put<T, T>(url, data, conf)

这里重点是通过二次封装,提供了一个单例请求对象。

拦截器

这里的拦截器都是针对当前业务的特例处理函数, 之所以以单一函数插件的形式导出,是希望尽量降低底层包与业务的相关性

代码语言:javascript
复制
// 请求拦截
// 设置请求头 token
export function setToken(getToken:() => string):AxiosTransformer{
  return (data:any, headers:any) => {
    headers.token = getToken()
    return JSON.stringify(data)
  }
}
// 响应拦截
// 因原请求接口返回数据存在格式不统一的请求,所以响应拦截的主要目的是统一响应数据格式
export class ReqData<T>{
  constructor(public _status:number, public _message:string, public data?:T) {
    this._status = _status
    this._message = _message
    this.data = data
  }
}


export interface IresData{
  code?:number
  status?:string
  message?:string
  [key:string]:any
}


export function resParse(response:string):IresData{
  // 存在请求成功后无返回
  try {
    return response ? JSON.parse(response) as IresData : {}
  } catch (error) {
    //适配 直接返回状态字符?
    console.error(`JSON 解析错误: ${error} => ${response}`)
    return {_message: typeof response === 'string' ? response : ''}
  }
}
....

其实单一的http,基本能满足基座与子应用请求对象的一致性。因为使用了单例模式, 子应嵌入基座时,基座与子应用使用同一依赖包,new MicroHttp(conf) 将返回同一请求对象。 也不需要通过传递请求对象保证配置的一致性

API 包

这个包主要提供多个应用或组件通用的请求函数, 是对http包的再次封装,与业务又强相关性。

目录

  • api
    • src
      • http.ts
      • index.ts
      • api-1
      • api-2
      • .....

http.ts

代码语言:javascript
复制
import {
  MicroHttp, 
  resAdapter, 
  resListAdapter,
  setToken,
  getDefaultConf,
  resParse,
  resErr,
  resServerErr
} from '@micro/http'
import cookies from 'js-cookie'
const inspect = Symbol.for('nodejs.util.inspect.custom');


const conf = {
  ...getDefaultConf(), 
  baseURL: process.env.NODE_ENV !== 'production' ? '/proxyApi/' : '/',
  transformResponse: [ 
    resParse,
    resAdapter,
    resListAdapter
  ],
  transformRequest: [
    setToken(() => {
      return cookies.get('token') || ''
    })
  ]
}


let _http:MicroHttp
const HTTP = new Proxy({} as MicroHttp, {
  get(_, propKey:keyof MicroHttp | typeof inspect){


    if(propKey === inspect) {
      return `HTTP Proxy`
    }
    
    if (!_http) {
      throw new Error('调用 HTTP,需要先使用 createHTTP 或 setHTTP 初始化请求对象')
    }
    return _http[propKey]
  },
  set(_, propKey:keyof MicroHttp, value:any){
    _http[propKey] = value
    return true
  } 
})




function setHTTP(http:MicroHttp):void{
  _http = http
}


function createHTTP(axios: any): void {
  setHTTP(new MicroHttp(axios.create(conf)))
  HTTP.interceptors.response.use(resErr, resServerErr)


}


function getHTTP():MicroHttp {
  return _http
}


export {
  HTTP,
  createHTTP,
  setHTTP,
  g

使用

代码语言:javascript
复制
import { createHTTP, HTTP } from '@micro/api'
import Axios from 'axios'
createHTTP(Axios)
HTTP.GET()
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 目标
  • 目录
  • http 包
    • 目录
      • 核心
        • 拦截器
        • API 包
          • 目录
            • http.ts
            • 使用
            相关产品与服务
            消息队列 TDMQ
            消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档