rabbit-harmony/common/src/main/ets/provider/RequestInterceptor.ets

212 lines
7.6 KiB
Plaintext
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 { InternalAxiosRequestConfig, AxiosResponse, FormData } from '@ohos/axios';
import { Signature } from '../constants/AppConstants';
import { Logger } from '../utils/Logger';
import { RandomUtil, MD5, JSONUtil, StrUtil } from '@pura/harmony-utils';
import systemDateTime from '@ohos.systemDateTime';
export interface EncryptConfig extends InternalAxiosRequestConfig {
startTime?: number;
params?: Record<string, string | number | boolean | null | undefined>;
}
export class RequestInterceptor {
static readonly TAG: string = "RabbitLog_Request";
static async onPrepare(config: EncryptConfig): Promise<EncryptConfig> {
config.startTime = Date.now();
// 1. 获取请求方法
const method = (config.method ?? "get").toLowerCase();
// 2. 准备基础参数 (Query Params)
config.params = config.params || {};
config.params.nonce = RandomUtil.generateUUID36()
config.params.timestamp = Math.trunc(systemDateTime.getTime() / 1000)
// 3. 对 Query 参数进行字典排序并拼接
let paramsMap = JSONUtil.jsonToMap(JSON.stringify(config.params));
let arrayMap = Array.from(paramsMap);
arrayMap.sort((a, b) => {
return a[0].localeCompare(b[0])
})
paramsMap = new Map(arrayMap)
let sortQueryString = "";
paramsMap.forEach((value, key) => {
sortQueryString += key + "=" + value + "&"
})
sortQueryString = encodeURI(sortQueryString.substring(0, sortQueryString.length - 1))
// 4. 准备签名盐值
let signature = MD5.digestSync(sortQueryString + '&' + MD5.digestSync(Signature));
// 5. 【核心判断】识别是否为文件上传
const contentType = String(config.headers?.['Content-Type'] || "");
const isFormData = config.data instanceof FormData || contentType.includes('multipart/form-data');
// 6. 根据请求类型和内容计算签名
if ((method === 'post' || method === 'put') && config.data) {
if(!isFormData){
let dataStr = JSON.stringify(config.data);
if (StrUtil.isNotEmpty(dataStr)) {
signature = MD5.digestSync(sortQueryString + '&' + dataStr + "&" + MD5.digestSync(Signature));
}
}
}
// 7. 回填带签名的参数
config.params.signature = signature;
// 8. 打印请求日志
RequestInterceptor.logRequest(config, isFormData);
return config;
}
static onResponse(response: AxiosResponse): AxiosResponse {
const config = response.config as EncryptConfig;
const startTime: number = config.startTime ?? Date.now();
RequestInterceptor.logResponse(response, startTime);
return response;
}
private static logRequest(config: EncryptConfig, isFormData: boolean): void {
let logString = `\n┌─── 📤 Request [ ${config.method?.toUpperCase()} ] ───\n`;
logString += `│ Url: ${config.baseURL}${config.url}\n`;
logString += `│ Final Params: ${JSON.stringify(config.params)}\n`;
if (isFormData) {
logString += `│ Body: [ Multipart/FormData - File Upload ]\n`;
} else {
logString += `│ Body: ${JSON.stringify(config.data)}\n`;
}
logString += `└─────────────────────────────────────\n`;
Logger.info(RequestInterceptor.TAG, logString);
}
private static logResponse(response: AxiosResponse, startTime: number): void {
const duration: number = Date.now() - startTime;
const config = response.config as EncryptConfig;
// 0. 完整的请求地址(包含参数拼接)
let fullUrl = `${config.baseURL ?? ""}${config.url ?? ""}`;
const params = config.params;
let queryStr = "";
if (params !== undefined && params !== null) {
const keys = Object.keys(params);
if (keys.length > 0) {
const queryParts: string[] = [];
for (const key of keys) {
const value = params[key];
if (value !== undefined && value !== null) {
queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
}
}
queryStr = queryParts.join('&');
const separator = fullUrl.includes('?') ? '&' : '?';
fullUrl += `${separator}${queryStr}`;
}
}
// 1. 获取完整的 Data 字符串
const dataStr: string = JSON.stringify(response.data);
// 2. 构造基础信息
let header ='';
header =`\n┌─── 📥 响应开始🔜Response [ ${response.status} ] [ ${duration}ms ] ───\n`;
header += `│ Full URL: ${fullUrl}\n`;
header += `│ Data Content: `;
// 3. 拼接完整报文
const fullLog = header + dataStr + `\n└────────────────────响应结束🔚─────────────────`;
// 4. 执行分段连贯打印
RequestInterceptor.printInChunks(fullLog);
}
/**
* 分段打印并确保数据完全打印
*/
private static printInChunks(message: string): void {
const CHUNK_SIZE = 3000;
let start = 0;
while (start < message.length) {
let end = Math.min(start + CHUNK_SIZE, message.length);
let chunk = message.substring(start, end);
Logger.info(RequestInterceptor.TAG, chunk);
start = end;
}
}
/*
private static logResponse(response: AxiosResponse, startTime: number): void {
const duration: number = Date.now() - startTime;
const config = response.config as EncryptConfig; // 显式类型转换
// 1. 安全地获取 BaseURL 和 URL
const baseUrl: string = config.baseURL ?? "";
let urlPath: string = config.url ?? "";
let fullUrl: string = `${baseUrl}${urlPath}`;
// 2. 强类型处理 Query Params (对应 Android 的 request.url().toString())
const params = config.params;
if (params !== undefined && params !== null) {
const keys: string[] = Object.keys(params);
if (keys.length > 0) {
const queryParts: string[] = [];
for (const key of keys) {
const value = params[key];
if (value !== undefined && value !== null) {
queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
}
}
const queryString: string = queryParts.join('&');
const separator: string = fullUrl.includes('?') ? '&' : '?';
fullUrl += `${separator}${queryString}`;
}
}
// 3. 构造日志字符串
let logString =''
logString =`\n┌─── 📥 Response [ ${String(response.status)} ] ───\n`;
logString += `│ Time: ${String(duration)}ms\n`;
logString += `│ Full Request URL: ${fullUrl}\n`;
// 4. 处理 Response Data (禁止使用 any需转换为 string)
const dataObj = response.data as object; // 假设返回的是对象
const dataStr: string = JSON.stringify(dataObj);
const displayData: string = dataStr.length > 1000
? `${dataStr.substring(0, 1000)}... [Truncated]`
: dataStr;
logString += `│ Data: ${displayData}\n`;
logString += `└─────────────────────────────────────\n`;
Logger.info(RequestInterceptor.TAG, logString);
}
*/
}
/**
* 时间同步工具
*/
export class TimeSync {
static timeOffset: number = 0; // 服务器时间 - 本地时间
static async sync(serverTimeStr: string) {
const serverTime = parseInt(serverTimeStr, 10);
const localTime = Math.trunc(Date.now() / 1000);
TimeSync.timeOffset = serverTime - localTime;
console.info(`[TimeSync] Offset calculated: ${TimeSync.timeOffset}s`);
}
static getCorrectedTime(): number {
// return Math.trunc(Date.now() / 1000) + TimeSync.timeOffset;
return Math.trunc(systemDateTime.getTime() / 1000);
}
}