egg_server/utils/netTool.ts
zhaoyingbo 09e352a9c1
All checks were successful
Egg Server MIflow / build-image (push) Successful in 1m5s
feat: 抽象网络请求类 & 内容转为ctx向内传递
2024-08-16 09:12:11 +00:00

425 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Logger } from "winston"
import loggerIns from "../log"
interface NetRequestParams {
url: string
method: string
queryParams?: any
payload?: any
additionalHeaders?: any
}
interface NetErrorDetail {
httpStatus: number
code: number
message: string
}
export class NetError extends Error {
public code: number
public message: string
public httpStatus: number
constructor({ code, message, httpStatus }: NetErrorDetail) {
super(message)
this.code = code
this.message = message
this.httpStatus = httpStatus
}
}
/**
* 网络工具类提供发送HTTP请求的方法。
*/
class NetToolBase {
protected prefix: string
protected headers: any
protected getHeaders: () => any
protected logger: Logger
protected requestId: string
/**
* 创建一个网络工具类实例。
*
* @param {Object} params - 构造函数参数。
* @param {string} [params.prefix] - URL前缀。
* @param {any} [params.headers] - 默认请求头。
* @param {Function} [params.getHeaders] - 获取请求头的方法。
* @param {string} [params.requestId] - 请求ID。
*/
constructor({
prefix,
headers,
getHeaders,
requestId,
}: {
prefix?: string
headers?: any
getHeaders?: () => any
requestId?: string
} = {}) {
this.prefix = prefix || ""
this.headers = headers || {}
this.getHeaders = getHeaders || (() => ({}))
this.requestId = requestId || ""
this.logger = loggerIns.child({ requestId })
}
/**
* 记录响应详情并返回响应日志对象。
* @param response - 响应对象。
* @param method - 请求使用的HTTP方法。
* @param headers - 请求头。
* @param requestBody - 请求体。
* @param responseBody - 响应体。
* @returns 响应日志对象。
*/
private logResponse(
response: Response,
method: string,
headers: any,
requestBody: any,
responseBody: any
) {
const responseLog = {
ok: response.ok,
status: response.status,
statusText: response.statusText,
url: response.url,
method: method,
requestHeaders: headers,
responseHeaders: response.headers,
requestBody,
responseBody,
}
this.logger.http(JSON.stringify(responseLog, null, 2))
return responseLog
}
/**
* 发送网络请求并返回一个解析为响应数据的Promise。
* @param url - 要发送请求的URL。
* @param method - 请求使用的HTTP方法。
* @param queryParams - 要包含在URL中的查询参数。
* @param payload - 请求的有效负载数据。
* @param additionalHeaders - 要包含在请求中的附加头。
* @returns 一个解析为响应数据的Promise。
* @throws 如果网络响应不成功或存在解析错误,则抛出错误。
*/
protected async request<T = any>({
url,
method,
queryParams,
payload,
additionalHeaders,
}: NetRequestParams): Promise<T> {
// 拼接完整的URL
let fullUrl = `${this.prefix}${url}`
if (queryParams) {
if (typeof queryParams === "string") {
fullUrl = `${fullUrl}?${queryParams}`
} else {
const queryString = new URLSearchParams(queryParams).toString()
if (queryString) fullUrl = `${fullUrl}?${queryString}`
}
}
// 设置请求头
const headers = {
...this.headers,
...(await this.getHeaders()),
...additionalHeaders,
}
// 设置请求Header
if (!(payload instanceof FormData)) {
headers["Content-Type"] = "application/json"
}
// 处理请求数据
const body = payload instanceof FormData ? payload : JSON.stringify(payload)
// 发送请求
const res = await fetch(fullUrl, {
method,
body,
headers,
})
// 获取响应数据
let resData: any = null
let resText: string = ""
try {
resText = await res.text()
resData = JSON.parse(resText)
} catch {
/* empty */
}
// 记录响应
this.logResponse(res, method, headers, payload, resData || resText)
if (!res.ok) {
if (resData?.message || resData?.msg) {
throw new NetError({
httpStatus: res.status,
code: resData?.code,
message: resData?.message || resData?.msg,
})
}
throw new NetError({
httpStatus: res.status,
code: res.status,
message: resText || "网络响应异常",
})
}
// http 错误码正常,但解析异常
if (!resData) {
throw new NetError({
httpStatus: res.status,
code: 1,
message: "解析响应数据异常",
})
}
// 响应数据异常
if ("code" in resData && resData.code !== 0) {
throw new NetError({
httpStatus: res.status,
code: resData.code,
message: resData.message || resData.msg || "网络请求失败",
})
}
return resData as T
}
/**
* 发送GET请求并返回一个解析为响应数据的Promise。
*
* @param url - 要发送请求的URL。
* @param queryParams - 要包含在URL中的查询参数。
* @param additionalHeaders - 要包含在请求中的附加头。
* @returns 一个解析为响应数据的Promise。
*/
protected get<T = any>(
url: string,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return this.request({ url, method: "get", queryParams, additionalHeaders })
}
/**
* 发送POST请求并返回一个解析为响应数据的Promise。
*
* @param url - 要发送请求的URL。
* @param payload - 请求的有效负载数据。
* @param queryParams - 要包含在URL中的查询参数。
* @param additionalHeaders - 要包含在请求中的附加头。
* @returns 一个解析为响应数据的Promise。
*/
protected post<T = any>(
url: string,
payload?: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return this.request({
url,
method: "post",
payload,
queryParams,
additionalHeaders,
})
}
/**
* 发送PUT请求并返回一个解析为响应数据的Promise。
*
* @param url - 要发送请求的URL。
* @param payload - 请求的有效负载数据。
* @param queryParams - 要包含在URL中的查询参数。
* @param additionalHeaders - 要包含在请求中的附加头。
* @returns 一个解析为响应数据的Promise。
*/
protected put<T = any>(
url: string,
payload: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return this.request({
url,
method: "put",
payload,
queryParams,
additionalHeaders,
})
}
/**
* 发送DELETE请求并返回一个解析为响应数据的Promise。
*
* @param url - 要发送请求的URL。
* @param payload - 请求的有效负载数据。
* @param queryParams - 要包含在URL中的查询参数。
* @param additionalHeaders - 要包含在请求中的附加头。
* @returns 一个解析为响应数据的Promise。
*/
protected del<T = any>(
url: string,
payload: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return this.request({
url,
method: "delete",
payload,
queryParams,
additionalHeaders,
})
}
/**
* 发送PATCH请求并返回一个解析为响应数据的Promise。
*
* @param url - 要发送请求的URL。
* @param payload - 请求的有效负载数据。
* @param queryParams - 要包含在URL中的查询参数。
* @param additionalHeaders - 要包含在请求中的附加头。
* @returns 一个解析为响应数据的Promise。
*/
protected patch<T = any>(
url: string,
payload: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return this.request({
url,
method: "patch",
payload,
queryParams,
additionalHeaders,
})
}
}
class NetTool extends NetToolBase {
public request<T = any>({
url,
method,
queryParams,
payload,
additionalHeaders,
}: NetRequestParams): Promise<T> {
return super.request<T>({
url,
method,
queryParams,
payload,
additionalHeaders,
})
}
public get<T = any>(
url: string,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return super.get<T>(url, queryParams, additionalHeaders)
}
public post<T = any>(
url: string,
payload?: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return super.post<T>(url, payload, queryParams, additionalHeaders)
}
public put<T = any>(
url: string,
payload: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return super.put<T>(url, payload, queryParams, additionalHeaders)
}
public del<T = any>(
url: string,
payload: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return super.del<T>(url, payload, queryParams, additionalHeaders)
}
public patch<T = any>(
url: string,
payload: any,
queryParams?: any,
additionalHeaders?: any
): Promise<T> {
return super.patch<T>(url, payload, queryParams, additionalHeaders)
}
/**
* 创建一个表示400 Bad Request的响应对象。
*
* @param message - 错误消息。
* @returns 一个表示400 Bad Request的响应对象。
*/
badRequest(message: string) {
this.logger.error(`return a bad request response: ${message}`)
return Response.json(
{ code: 400, message, requestId: this.requestId },
{ status: 400 }
)
}
/**
* 创建一个表示404 Not Found的响应对象。
*
* @param message - 错误消息。
* @returns 一个表示404 Not Found的响应对象。
*/
notFound(message: string) {
this.logger.error(`return a not found response: ${message}`)
return Response.json(
{ code: 404, message, requestId: this.requestId },
{ status: 404 }
)
}
/**
* 创建一个表示500 Internal Server Error的响应对象。
*
* @param message - 错误消息。
* @param data - 错误数据。
* @returns 一个表示500 Internal Server Error的响应对象。
*/
serverError(message: string, data?: any) {
this.logger.error(`return a server error response: ${message}`)
return Response.json(
{ code: 500, message, data, requestId: this.requestId },
{ status: 500 }
)
}
/**
* 创建一个表示200 OK的响应对象。
*
* @param data - 响应数据。
* @returns 一个表示200 OK的响应对象。
*/
ok(data?: any) {
this.logger.info(`return a ok response: ${JSON.stringify(data)}`)
return Response.json({
code: 0,
message: "success",
data,
requestId: this.requestId,
})
}
}
export { NetToolBase }
export default NetTool