feat(common): 新增基础设施 HAR(网络/鉴权/日志/状态/工具)
This commit is contained in:
26
common/src/main/ets/network/ApiError.ets
Normal file
26
common/src/main/ets/network/ApiError.ets
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 统一错误模型:业务错误码 / HTTP 状态码 / 网络异常。
|
||||
* 继承 Error 以符合 ArkTS 的 throw 限制(只能抛 Error 及其子类)。
|
||||
*/
|
||||
export class ApiError extends Error {
|
||||
readonly code: number;
|
||||
readonly isNetworkError: boolean;
|
||||
|
||||
constructor(code: number, message: string, isNetworkError: boolean = false) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.isNetworkError = isNetworkError;
|
||||
}
|
||||
|
||||
static network(detail: string): ApiError {
|
||||
return new ApiError(-1, `网络请求失败: ${detail}`, true);
|
||||
}
|
||||
|
||||
static http(statusCode: number): ApiError {
|
||||
return new ApiError(statusCode, `HTTP 状态码异常: ${statusCode}`, false);
|
||||
}
|
||||
|
||||
static business(code: number, message: string): ApiError {
|
||||
return new ApiError(code, message, false);
|
||||
}
|
||||
}
|
||||
18
common/src/main/ets/network/ApiResult.ets
Normal file
18
common/src/main/ets/network/ApiResult.ets
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 统一响应包装:与后端约定 code / message / data 结构。
|
||||
*/
|
||||
export class ApiResult<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
|
||||
constructor(code: number, message: string, data: T) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
isSuccess(): boolean {
|
||||
return this.code === 0;
|
||||
}
|
||||
}
|
||||
24
common/src/main/ets/network/AuthInterceptor.ets
Normal file
24
common/src/main/ets/network/AuthInterceptor.ets
Normal file
@@ -0,0 +1,24 @@
|
||||
import { HttpInterceptor } from './HttpInterceptor';
|
||||
import { SessionManager } from '../state/SessionStore';
|
||||
|
||||
/**
|
||||
* 鉴权拦截器:请求前注入 Authorization 头;401 时清空会话(登出)。
|
||||
*
|
||||
* TODO(鉴权): 401 时的 token 刷新 + 原请求重放(防并发刷新)需在
|
||||
* 后端 refresh 接口约定后实现,当前先清空会话交由上层跳登录。
|
||||
*/
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
onRequest(header: Record<string, string>): Record<string, string> {
|
||||
const session = SessionManager.get();
|
||||
if (session.token.length > 0) {
|
||||
header['Authorization'] = `Bearer ${session.token}`;
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
onResponse(responseCode: number, result: string): void {
|
||||
if (responseCode === 401) {
|
||||
SessionManager.get().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
65
common/src/main/ets/network/HttpClient.ets
Normal file
65
common/src/main/ets/network/HttpClient.ets
Normal file
@@ -0,0 +1,65 @@
|
||||
import { http } from '@kit.NetworkKit';
|
||||
import { HttpInterceptor } from './HttpInterceptor';
|
||||
import { ApiError } from './ApiError';
|
||||
|
||||
/**
|
||||
* HTTP 客户端封装:统一超时、请求头、拦截器链。
|
||||
* 基于 @kit.NetworkKit 的 http 模块(Promise 风格)。
|
||||
*/
|
||||
export class HttpClient {
|
||||
private readonly client: http.HttpRequest = http.createHttp();
|
||||
private interceptors: HttpInterceptor[] = [];
|
||||
|
||||
addInterceptor(interceptor: HttpInterceptor): HttpClient {
|
||||
this.interceptors.push(interceptor);
|
||||
return this;
|
||||
}
|
||||
|
||||
async get(url: string, header?: Record<string, string>): Promise<string> {
|
||||
return this.request(url, http.RequestMethod.GET, undefined, header);
|
||||
}
|
||||
|
||||
async post(url: string, body: string, header?: Record<string, string>): Promise<string> {
|
||||
return this.request(url, http.RequestMethod.POST, body, header);
|
||||
}
|
||||
|
||||
async put(url: string, body: string, header?: Record<string, string>): Promise<string> {
|
||||
return this.request(url, http.RequestMethod.PUT, body, header);
|
||||
}
|
||||
|
||||
async delete(url: string, header?: Record<string, string>): Promise<string> {
|
||||
return this.request(url, http.RequestMethod.DELETE, undefined, header);
|
||||
}
|
||||
|
||||
private async request(url: string, method: http.RequestMethod, body?: string,
|
||||
header?: Record<string, string>): Promise<string> {
|
||||
let headers: Record<string, string> = {};
|
||||
if (header !== undefined) {
|
||||
headers = header;
|
||||
}
|
||||
for (const interceptor of this.interceptors) {
|
||||
headers = interceptor.onRequest(headers);
|
||||
}
|
||||
|
||||
const options: http.HttpRequestOptions = {
|
||||
method: method,
|
||||
header: headers,
|
||||
expectDataType: http.HttpDataType.STRING,
|
||||
connectTimeout: 10000,
|
||||
readTimeout: 10000
|
||||
};
|
||||
if (body !== undefined) {
|
||||
options.extraData = body;
|
||||
}
|
||||
|
||||
try {
|
||||
const response: http.HttpResponse = await this.client.request(url, options);
|
||||
for (const interceptor of this.interceptors) {
|
||||
interceptor.onResponse(response.responseCode, response.result as string);
|
||||
}
|
||||
return response.result as string;
|
||||
} catch (err) {
|
||||
throw ApiError.network(JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
10
common/src/main/ets/network/HttpInterceptor.ets
Normal file
10
common/src/main/ets/network/HttpInterceptor.ets
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 请求/响应拦截器接口。
|
||||
* 实现类可注入请求头(鉴权)、统一处理响应(401/错误码)等。
|
||||
*/
|
||||
export interface HttpInterceptor {
|
||||
/** 请求发出前,返回(可修改的)请求头。 */
|
||||
onRequest(header: Record<string, string>): Record<string, string>;
|
||||
/** 响应返回后,可据此抛错中断(如 401 触发登出)。 */
|
||||
onResponse(responseCode: number, result: string): void;
|
||||
}
|
||||
Reference in New Issue
Block a user