Skip to content

如何修改请求后台

修改后台地址

项目中默认提供了三种请求环境,如果需要修改,可以修改src\typings\env.d.ts文件,增加ServiceEnvType类型

ts
type ServiceEnvType = 'dev' | 'test' | 'prod'

service.config.ts文件中配置不同的后台地址。如下例子,dev为开发环境,test为测试环境,prod为生产环境,为每个环境下配置了不同的后台地址

ts
export const serviceConfig: Record<ServiceEnvType, Record<string, string>> = {
  dev: {
    url: 'dev_url',
  },
  test: {
    url: 'test_url',
  },
  prod: {
    url: 'prod_url',
  },
}

使用代理

在一些情况下,可能无法去访问到后台地址,这时候可以使用代理来访问后台。在本项目中你可以很容易切换到代理环境

shell
# 是否开启服务接口代理 Y | N
VITE_HTTP_PROXY=Y

如下配置, 开启代理后,会自动将请求地址修改为代理地址

ts
import { createAlovaInstance } from './alova'
import { serviceConfig } from '@/../service.config'
import { generateProxyPattern } from '@/../build/proxy'

const isHttpProxy = import.meta.env.VITE_HTTP_PROXY === 'Y' || false

const { url } = generateProxyPattern(serviceConfig[import.meta.env.MODE])

export const request = createAlovaInstance({
  baseURL: isHttpProxy ? url.proxy : url.value,
})

此处解构出的url和必须和上方service.config.ts中的字段保持一致,例如

ts
dev: {
  otherUrl: 'dev_url',
}
ts
const { otherUrl } = generateProxyPattern(serviceConfig[import.meta.env.MODE])

使用多个服务

在一些情况下,可能需要使用多个后台服务地址,例如a,b,c接口请求后台地址A,而d,e,f接口请求后台地址B,此时你可以像这样配置

ts
dev: {
  url_A: 'dev_url_A',
  url_B: 'dev_url_B',
}
ts
const { url_A, url_B } = generateProxyPattern(serviceConfig[import.meta.env.MODE])

export const requestA = createAlovaInstance({
  baseURL: isHttpProxy ? url_A.proxy : url_A.value,
})

export const requestB = createAlovaInstance({
  baseURL: isHttpProxy ? url_B.proxy : url_B.value,
})

对于不同的服务后台,可能他们响应的字段也不一样,例如

js
// service A success response
{
  code: 200,
  data: 'some data',
  message: 'success',
}

// service B success response
{
  status: 1,
  data: 'some data',
  msg:'success'
}

此时你可以传入第二个参数来解决这个问题

ts
// src\service\http\index.ts
const { url_A, url_B } = generateProxyPattern(serviceConfig[import.meta.env.MODE])

export const requestA = createAlovaInstance({
  baseURL: isHttpProxy ? url_A.proxy : url_A.value,
})

export const requestB = createAlovaInstance({
  baseURL: isHttpProxy ? url_B.proxy : url_B.value,
}, {
  codeKey: 'status',
  dataKey: 'data',
  msgKey: 'msg',
  successCode: 1,
})

定义请求

在项目中,一般需要在src\service\api中新建一个文件定义你的请求方法,然后导出,如下示例

ts
import { request } from '../http'

export function fetchUserList() {
  return request.Get('/userList')
}
ts
export * from './api/list'

然后在页面或其他地方引入使用

ts
import { fetchUserList } from '@/service'

const listData = ref()
async function getUserList() {
  await fetchUserList().then((res: any) => {
    listData.value = res.data.list
  })
}
getUserList()

上面是最简单的请求方法使用示例,但是这样做响应数据res的类型为any,如果需要正确获取响应数据的类型,你可以像这样定义,来规定传入参数和返回参数的类型

ts
interface MyList{
  // ...
}
export function fetchUserList(params: { id: number }) {
  return request.Get<Service.ResponseResult<MyList> >('/userList', { params })
}
ResponseResult 类型声明
ts
// Service.ResponseResult
interface ResponseResult<T> extends RequestError {
  /** 请求服务是否成功 */
  isSuccess: boolean
  /** 请求服务的错误类型 */
  errorType: RequestErrorType
  /** 错误码 */
  code: RequestCode
  /** 错误信息 */
  message: string
  /** 返回的数据 */
  data: T
}

其中Service.ResponseResult是请求响应数据的包裹,无需变动,需要改变其传入的泛型,如上例传入了MyList类型

ts
const { data } = await fetchUserList({id: 1})

此时data的类型被正确推导为MyList

请求使用方法

这里有一些定义不同的使用请求方法的例子,覆盖了大多数情况,你可以根据你的需求来选择使用

Get

ts
export function fetachGet(params?: any) {
  return request.Get('/getAPI', { params })
}

Post(json)

ts
export function fetchPost(data: any) {
  return request.Post('/postAPI', data)
}

Post(form)

Post方法默认为json格式,如果需要使用form格式,你可以这样定义,配置meta标识isFormPost,在发送请求的过程中会自动修改格式

ts
export function fetchFormPost(data: any) {
  const methodInstance = request.Post('/postFormAPI', data)
  methodInstance.meta = {
    isFormPost: true,
  }
  return methodInstance
}

Delete

ts
export function fetchDelete() {
  return request.Delete('/deleteAPI')
}

Put

ts
export function fetchPut(data: any) {
  return request.Put('/putAPI', data)
}

不需要携带token的接口

默认情况下,所有请求都会携带token,如果某些接口不需要携带token,你可以像这样定义

ts
export function withoutToken() {
  const methodInstance = request.Get('/getAPI')
  methodInstance.meta = {
    authRole: null,
  }
  return methodInstance
}

接口数据转换

一些时候,后台返回的数据可能需要进行转换,此时你可以像这样定义,在下面的例子中genderstatus返回后会被自动改写

ts
export function dictData() {
  return request.Get('/getDictData', {
    transform(rawData, _headers) {
      const response = rawData as any
      return {
        ...response,
        data: {
          ...response.data,
          gender: response.data.gender === 0 ? '男' : '女',
          status: `状态是${response.data.status}`,
        },
      }
    },
  })
}

获取二进制文件

一些时候,后台返回的数据是文件流,需要直接下载,此时你可以像这样定义。标识为isBlob后关于这个接口的请求会自动进行相关处理

ts
export function getBlob(url: string) {
  const methodInstance = blankInstance.Get<Blob>(url)
  methodInstance.meta = {
    // 标识为bolb数据
    isBlob: true,
  }
  return methodInstance
}

一个简单的调用并保存的示例

ts
function getBlobFile() {
  getBlob(filePath.value).then((res) => {
    downloadLink(res, 'BlobOk')
  })
}
function downloadLink(data: Blob, name: string) {
  const link = URL.createObjectURL(data)
  const eleLink = document.createElement('a')
  eleLink.download = name
  eleLink.style.display = 'none'
  eleLink.href = link
  document.body.appendChild(eleLink)
  eleLink.click()
  document.body.removeChild(eleLink)
}
// 调用
getBlobFile()

进度下载

有时后台返回的大文件需要下载进度,则这样定义

ts
export function downloadFile(url: string) {
  const methodInstance = blankInstance.Get<Blob>(url)
  methodInstance.meta = {
    // 标识为bolb数据
    isBlob: true,
  }
  return methodInstance
}

需要使用useRequest来进行包裹,获取下载进度对象

ts
import { useRequest } from 'alova'

// downloading - 下载进度对象
// abort - 取消下载
// send - 重新发送下载请求
const { downloading, abort: abortDownloadFile, send: sendDownloadFile } = useRequest(downloadFile(downloadPath.value), {
  // 当immediate为false时,默认不发出
  immediate: false,
})
// 使用computed自动计算进度
const downloadProcess = computed(() => {
  if (!downloading.value.loaded)
    return 0
  return Math.floor(downloading.value.loaded / downloading.value.total * 100)
})
// 对请求结果保存成文件
async function handleDownloadFile() {
  const res = await sendDownloadFile()
  downloadLink(res, 'fileOk')
}

更多使用方法

本项目的请求方法基于Alova进行封装,更多使用方法请参考Alova

请求器配置

src\service\http\config.ts中,你可以修改请求器的判断配置。修改这些配置会影响请求器实例的内部判断流程

DEFAULT_ALOVA_OPTIONS

修改该字段让请求器增加默认配置,字段详见alova.options

DEFAULT_BACKEND_OPTIONS

修改该字段来更改请求的默认判断字段。

BackendConfig类型声明
ts
interface BackendConfig {
  codeKey?: string
  dataKey?: string
  msgKey?: string
  successCode?: number | string
}
  • codeKey: 业务码字段标识,默认为code
  • dataKey: 数据携带字段标识,默认为data
  • msgKey: 消息标识,默认为message
  • successCode: 成功标识码,默认为200

例如,在你对接的后台返回数据中,消息标识为_message,那么你应当修改msgKey来保持请求器内部自动处理的正确, codeKeydataKey同理,项目中的数据自动处理依赖这些字段配置

ERROR_STATUS

请求如果遇到服务器错误的情况,会自动根据状态码进行判断,如果状态码在ERROR_STATUS中,则判定为请求失败,如果不在,则提示默认错误

ERROR_STATUS
ts
export const ERROR_STATUS = {
  default: '请求错误~',
  400: '400: 请求出现语法错误~',
  401: '401: 用户未授权~',
  403: '403: 服务器拒绝访问~',
  404: '404: 请求的资源不存在~',
  405: '405: 请求方法未允许~',
  408: '408: 网络请求超时~',
  500: '500: 服务器内部错误~',
  501: '501: 服务器未实现请求功能~',
  502: '502: 错误网关~',
  503: '503: 服务不可用~',
  504: '504: 网关超时~',
  505: '505: http版本不支持该请求~',
}

ERROR_NO_TIP_STATUS

请求发出后,服务器返回了一个错误码,但是你不想让用户看到错误提示,你可以将该错误码添加到该数组中,例如默认情况下10000不会出现错误提示弹出

ts
export const ERROR_NO_TIP_STATUS = [10000]

基于 MIT 许可发布