Skip to content

Commit

Permalink
fix: request接口纯js实现
Browse files Browse the repository at this point in the history
  • Loading branch information
Pzx-00100 committed Mar 27, 2024
1 parent 0737c1d commit 6a40d31
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/taro-mpharmony/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@tarojs/shared": "workspace:*",
"@tarojs/taro-h5": "workspace:*",
"abortcontroller-polyfill": "^1.7.5",
"axios": "^1.6.8",
"base64-js": "^1.3.0",
"ics": "^3.1.0",
"intersection-observer": "^0.7.0",
Expand Down
237 changes: 227 additions & 10 deletions packages/taro-mpharmony/src/api/network/request/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,24 @@ import Taro from '@tarojs/api'
import { isFunction } from '@tarojs/shared'
import { getParameterError, shouldBeObject } from 'src/utils'

import { NativeRequest } from '../../interface/NativeRequest'
import native from '../../NativeApi'
const axios = require('axios').default

const CancelToken = axios.CancelToken
const source = CancelToken.source()

const errMsgMap = new Map([
[401, 'Parameter error'],
[201, 'Permission denied'],
[3, 'URL using bad/illegal format or missing URL'],
[7, "Couldn't connect to server"],
[23, 'Failed writing received data to disk/application'],
[25, 'Upload failed'],
[26, 'Failed to open/read local data from file/application'],
[28, 'Timeout was reached'],
[73, 'Remote file already exists'],
[78, 'Remote file not found'],
[999, 'Unknown Other Error']
])

export const _request = (options) => {
const name = 'request'
Expand All @@ -28,10 +44,10 @@ export const _request = (options) => {
return Promise.reject(res)
}

let task!: Taro.RequestTask<any>
let task: any
const result: ReturnType<typeof Taro.request> = new Promise((resolve, reject) => {
const upperMethod = method ? method.toUpperCase() : method
const taskID = native.request({
task = nativeRequest({
url,
method: upperMethod,
...otherOptions,
Expand All @@ -46,11 +62,8 @@ export const _request = (options) => {
reject(res)
},
})
// @ts-ignore
task = window.isOsChannel ? taskID : NativeRequest.getRequestTask(taskID)
}) as any


result.onHeadersReceived = task.onHeadersReceived.bind(task)
result.offHeadersReceived = task.offHeadersReceived.bind(task)
result.abort = task.abort.bind(task)
Expand All @@ -61,6 +74,11 @@ function taroInterceptor (chain) {
return _request(chain.requestParams)
}

function nativeRequest (...args) {
const obj = args.pop()
return new RequestTask(obj)
}

// @ts-ignore
const { Link } = Taro
const link = new Link(taroInterceptor)
Expand All @@ -69,12 +87,12 @@ const link = new Link(taroInterceptor)
* 发起 HTTPS 网络请求
*
* @canUse request
* @__object [url, data, header, timeout, method[OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT], responseType[text, arraybuffer], enableCache]
* @__object [url, data, header, timeout, method[OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT], dataType[text, json, arraybuffer, base64], responseType[text, arraybuffer], enableCache]
* @__success [data, header, statusCode, cookies]
*/
export function request (options) {
const result = link.request.bind(link)(options)
result.catch(() => {})
const result = link.request.bind(link)(options) // promise
result.catch(() => { })
return result
}

Expand All @@ -98,3 +116,202 @@ export const addInterceptor = link.addInterceptor.bind(link)
* @canNotUse cleanInterceptors
*/
export const cleanInterceptors = link.cleanInterceptors.bind(link)

class RequestTask {
public responseHeader
public abortFlag
public fail
public complete
public headersCallback
public result
public res
public interceptor
public httpRequest

constructor (object) {
const { url, headers, method, timeout, responseType, enableCache } = object || {}
let { data } = object || {}
const { success, fail, complete, dataType } = object || {}

this.responseHeader = null
this.abortFlag = false
this.fail = fail
this.complete = complete
this.headersCallback = new Set()
// 使用axios.create来创建axios实例
this.httpRequest = axios.create({
responseType: responseType || 'text',
headers: headers,
timeout: timeout || 2000
})

// 请求拦截器
this.httpRequest.interceptors.request.use((config) => {
if (config.enableCache === false) {
return config
}
// 处理缓存
const cacheData = localStorage.getItem(config.url)
if (cacheData !== null) {
let result = cacheData
if (dataType === 'json') {
result = JSON.parse(cacheData)
}
source.cancel('cache has useful data!!')
return Promise.resolve({ result })
}
return config
}, error => {
console.error('error: ', error)
})

// 响应拦截器
this.httpRequest.interceptors.response.use((response) => {
// 缓存数据
if (response.config.enableCache === false) {
localStorage.setItem(response.config.url, JSON.stringify(response.data))
}
return response
}, error => {
console.error('error: ', error)
})

if (!object) {
console.error('request error: params illegal')
return
}

let isFormUrlEncoded = false
for (const key in headers) {
if (key.toLowerCase() === 'content-type') {
if (headers[key].toLowerCase().includes('application/x-www-form-urlencoded')) {
isFormUrlEncoded = true
}
break
}
}

// data为Object类型时,属性的值类型如果是number, request请求时信息会丢失. 故将data转成string类型进行规避
if (data && (isFormUrlEncoded || ['GET', 'OPTIONS', 'DELETE', 'TRACE', 'CONNECT'].includes(method))) {
const dataArray = []
for (const key in data) {
// @ts-ignore
dataArray.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
}
data = dataArray.join('&')
}

// header的属性的值类型如果是number, request请求时信息会丢失. 故将各个属性转成string类型
if (headers) {
for (const key in headers) {
headers[key] = `${headers[key]}`
}
}

this.httpRequest({
method: method,
url: url,
CancelToken: source.token,
enableCache: enableCache || false
})
.then((response) => {
if (success && !this.abortFlag) {
this.responseHeader = response.headers
let result = response.result
if (response.config.responseType === 'text') {
if (dataType === 'text') {
result = response.data
} else if (dataType === 'json') {
result = JSON.parse(response.data)
} else if (dataType === 'base64') {
const encodeData = encodeURIComponent(response.data)
result = btoa(encodeData)
} else if (dataType === 'arraybuffer') {
result = new TextEncoder().encode(response.data).buffer
} else {
console.error('Unsupported dataType!!')
}
} else if (response.config.responseType === 'arraybuffer') {
result = response.data
} else {
console.error('Unsupported dataType!!: ', response.config.responseType)
}
const res = {
data: result,
statusCode: response.status,
header: response.headers,
cookies: response.cookies ? [response.cookies] : [],
errMsg: 'request:ok'
}
this.result = res
success(res)
}
})
.catch((err) => {
console.error('request error: ' + JSON.stringify(err))
if (fail && !this.abortFlag) {
// eslint-disable-next-line no-console
const res = { errMsg: errMsgMap.has(err.code) ? errMsgMap.get(err.code) : `${JSON.stringify(err)}` }
this.result = res
fail(res)
}
})
.finally(() => {
if (complete && !this.abortFlag) {
complete(this.result)
}
if (this.httpRequest) {
source.cancel('requestTask cancelled by the user!')
}
})
}

/**
* interrupt request task
*/
abort () {
this.abortFlag = true
if (this.httpRequest) {
source.cancel('requestTask cancelled by the user!')
this.res = { errMsg: 'request:fail abort' }
this.fail && this.fail(this.res)
this.complete && this.complete(this.res)
}
}

onHeadersReceived (callback) {
if (!callback) {
console.error('[AdvancedAPI] Invalid, callback is null')
return
}
const taskCallback = (header) => {
!this.abortFlag && callback({ header })
}
if (!this.headersCallback.has(callback)) {
this.headersCallback.add(taskCallback)
if (this.httpRequest) {
this.interceptor = this.httpRequest.interceptors.response.use((response) => {
taskCallback(this.responseHeader)
return response
})
}
taskCallback(this.responseHeader)
}
}

/**
* unsubscribe HTTP Response Header event
* remove all if callback is null, otherwise remove the specialized callback
*/
offHeadersReceived (callback) {
if (this.headersCallback.has(callback)) {
if (this.httpRequest) {
this.httpRequest.interceptors.eject(this.interceptor)
}
this.headersCallback.delete(callback)
} else {
// eslint-disable-next-line no-console
console.log('offHeadersReceived callback invalid')
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,12 @@
"TRACE": true,
"CONNECT": true
},
"dataType": {
"text": true,
"json": true,
"arraybuffer": true,
"base64": true
},
"responseType": {
"text": true,
"arraybuffer": true
Expand Down
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6a40d31

Please sign in to comment.