feat: 完成第一版,加入netTool以及logger
This commit is contained in:
commit
2ad41355a9
0
.devcontainer/devcontainer.json
Normal file
0
.devcontainer/devcontainer.json
Normal file
58
.gitignore
vendored
Normal file
58
.gitignore
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# 0x
|
||||
profile-*
|
||||
|
||||
# mac files
|
||||
.DS_Store
|
||||
|
||||
# vim swap files
|
||||
*.swp
|
||||
|
||||
# webstorm
|
||||
.idea
|
||||
|
||||
# vscode
|
||||
# .vscode
|
||||
*code-workspace
|
||||
|
||||
# clinic
|
||||
profile*
|
||||
*clinic*
|
||||
*flamegraph*
|
1
.husky/commit-msg
Normal file
1
.husky/commit-msg
Normal file
@ -0,0 +1 @@
|
||||
npx --no -- commitlint --edit $1
|
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@ -0,0 +1 @@
|
||||
lint-staged
|
5
.npmignore
Normal file
5
.npmignore
Normal file
@ -0,0 +1,5 @@
|
||||
src
|
||||
node_modules
|
||||
.git
|
||||
.vscode
|
||||
.devcontainer
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"cSpell.words": ["commitlint", "tseslint"]
|
||||
}
|
1
commitlint.config.js
Normal file
1
commitlint.config.js
Normal file
@ -0,0 +1 @@
|
||||
export default { extends: ["@commitlint/config-conventional"] }
|
3
dist/index.d.ts
vendored
Normal file
3
dist/index.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import loggerIns from "./logger";
|
||||
import { NetTool, NetToolBase } from "./netTool";
|
||||
export { loggerIns, NetTool, NetToolBase };
|
3
dist/index.js
vendored
Normal file
3
dist/index.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import loggerIns from "./logger";
|
||||
import { NetTool, NetToolBase } from "./netTool";
|
||||
export { loggerIns, NetTool, NetToolBase };
|
5
dist/logger.d.ts
vendored
Normal file
5
dist/logger.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
import "winston-daily-rotate-file";
|
||||
|
||||
import winston from "winston";
|
||||
declare const loggerIns: winston.Logger;
|
||||
export default loggerIns;
|
42
dist/logger.js
vendored
Normal file
42
dist/logger.js
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
import "winston-daily-rotate-file";
|
||||
|
||||
import winston, { format } from "winston";
|
||||
const isProd = process.env.NODE_ENV === "production";
|
||||
const transports = [
|
||||
new winston.transports.Console({
|
||||
level: "info",
|
||||
}),
|
||||
];
|
||||
if (isProd) {
|
||||
const config = {
|
||||
datePattern: "YYYY-MM-DD",
|
||||
zippedArchive: true,
|
||||
maxSize: "20m",
|
||||
maxFiles: "14d",
|
||||
};
|
||||
transports.push(new winston.transports.DailyRotateFile({
|
||||
level: "info",
|
||||
filename: "/home/work/log/egg-info-%DATE%.log",
|
||||
...config,
|
||||
}));
|
||||
transports.push(new winston.transports.DailyRotateFile({
|
||||
level: "debug",
|
||||
filename: "/home/work/log/egg-debug-%DATE%.log",
|
||||
...config,
|
||||
}));
|
||||
}
|
||||
const loggerIns = winston.createLogger({
|
||||
level: "silly",
|
||||
format: format.combine(format.colorize({
|
||||
level: !isProd,
|
||||
}), // 开发环境下输出彩色日志
|
||||
format.simple(), // 简单文本格式化
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.printf(({ level, message, timestamp, requestId }) => {
|
||||
const singleLineMessage = isProd
|
||||
? message.replace(/\n/g, " ") // 将换行符替换为空格
|
||||
: message;
|
||||
return `${timestamp} [${level}]${requestId ? ` [RequestId: ${requestId}]` : ""}: ${singleLineMessage}`;
|
||||
})),
|
||||
transports,
|
||||
});
|
||||
export default loggerIns;
|
152
dist/netTool.d.ts
vendored
Normal file
152
dist/netTool.d.ts
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
import { Logger } from "winston";
|
||||
interface NetRequestParams {
|
||||
url: string;
|
||||
method: string;
|
||||
queryParams?: any;
|
||||
payload?: any;
|
||||
additionalHeaders?: any;
|
||||
}
|
||||
interface NetErrorDetail {
|
||||
httpStatus: number;
|
||||
code: number;
|
||||
message: string;
|
||||
}
|
||||
export declare class NetError extends Error {
|
||||
code: number;
|
||||
message: string;
|
||||
httpStatus: number;
|
||||
constructor({ code, message, httpStatus }: NetErrorDetail);
|
||||
}
|
||||
/**
|
||||
* 网络工具类,提供发送HTTP请求的方法。
|
||||
*/
|
||||
declare 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;
|
||||
});
|
||||
/**
|
||||
* 记录响应详情并返回响应日志对象。
|
||||
* @param response - 响应对象。
|
||||
* @param method - 请求使用的HTTP方法。
|
||||
* @param headers - 请求头。
|
||||
* @param requestBody - 请求体。
|
||||
* @param responseBody - 响应体。
|
||||
* @returns 响应日志对象。
|
||||
*/
|
||||
private logResponse;
|
||||
/**
|
||||
* 发送网络请求并返回一个解析为响应数据的Promise。
|
||||
* @param url - 要发送请求的URL。
|
||||
* @param method - 请求使用的HTTP方法。
|
||||
* @param queryParams - 要包含在URL中的查询参数。
|
||||
* @param payload - 请求的有效负载数据。
|
||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||
* @returns 一个解析为响应数据的Promise。
|
||||
* @throws 如果网络响应不成功或存在解析错误,则抛出错误。
|
||||
*/
|
||||
protected request<T = any>({ url, method, queryParams, payload, additionalHeaders, }: NetRequestParams): Promise<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>;
|
||||
/**
|
||||
* 发送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>;
|
||||
/**
|
||||
* 发送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>;
|
||||
/**
|
||||
* 发送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>;
|
||||
/**
|
||||
* 发送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>;
|
||||
}
|
||||
declare class NetTool extends NetToolBase {
|
||||
request<T = any>({ url, method, queryParams, payload, additionalHeaders, }: NetRequestParams): Promise<T>;
|
||||
get<T = any>(url: string, queryParams?: any, additionalHeaders?: any): Promise<T>;
|
||||
post<T = any>(url: string, payload?: any, queryParams?: any, additionalHeaders?: any): Promise<T>;
|
||||
put<T = any>(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise<T>;
|
||||
del<T = any>(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise<T>;
|
||||
patch<T = any>(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise<T>;
|
||||
/**
|
||||
* 创建一个表示400 Bad Request的响应对象。
|
||||
*
|
||||
* @param message - 错误消息。
|
||||
* @returns 一个表示400 Bad Request的响应对象。
|
||||
*/
|
||||
badRequest(message: string): import("undici-types").Response;
|
||||
/**
|
||||
* 创建一个表示404 Not Found的响应对象。
|
||||
*
|
||||
* @param message - 错误消息。
|
||||
* @returns 一个表示404 Not Found的响应对象。
|
||||
*/
|
||||
notFound(message: string): import("undici-types").Response;
|
||||
/**
|
||||
* 创建一个表示500 Internal Server Error的响应对象。
|
||||
*
|
||||
* @param message - 错误消息。
|
||||
* @param data - 错误数据。
|
||||
* @returns 一个表示500 Internal Server Error的响应对象。
|
||||
*/
|
||||
serverError(message: string, data?: any): import("undici-types").Response;
|
||||
/**
|
||||
* 创建一个表示200 OK的响应对象。
|
||||
*
|
||||
* @param data - 响应数据。
|
||||
* @returns 一个表示200 OK的响应对象。
|
||||
*/
|
||||
ok(data?: any): import("undici-types").Response;
|
||||
}
|
||||
export { NetTool, NetToolBase };
|
303
dist/netTool.js
vendored
Normal file
303
dist/netTool.js
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
import loggerIns from "./logger";
|
||||
export class NetError extends Error {
|
||||
code;
|
||||
message;
|
||||
httpStatus;
|
||||
constructor({ code, message, httpStatus }) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.httpStatus = httpStatus;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 网络工具类,提供发送HTTP请求的方法。
|
||||
*/
|
||||
class NetToolBase {
|
||||
prefix;
|
||||
headers;
|
||||
getHeaders;
|
||||
logger;
|
||||
requestId;
|
||||
/**
|
||||
* 创建一个网络工具类实例。
|
||||
*
|
||||
* @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, } = {}) {
|
||||
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 响应日志对象。
|
||||
*/
|
||||
logResponse(response, method, headers, requestBody, responseBody) {
|
||||
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 如果网络响应不成功或存在解析错误,则抛出错误。
|
||||
*/
|
||||
async request({ url, method, queryParams, payload, additionalHeaders, }) {
|
||||
// 拼接完整的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 = null;
|
||||
let resText = "";
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* 发送GET请求并返回一个解析为响应数据的Promise。
|
||||
*
|
||||
* @param url - 要发送请求的URL。
|
||||
* @param queryParams - 要包含在URL中的查询参数。
|
||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||
* @returns 一个解析为响应数据的Promise。
|
||||
*/
|
||||
get(url, queryParams, additionalHeaders) {
|
||||
return this.request({ url, method: "get", queryParams, additionalHeaders });
|
||||
}
|
||||
/**
|
||||
* 发送POST请求并返回一个解析为响应数据的Promise。
|
||||
*
|
||||
* @param url - 要发送请求的URL。
|
||||
* @param payload - 请求的有效负载数据。
|
||||
* @param queryParams - 要包含在URL中的查询参数。
|
||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||
* @returns 一个解析为响应数据的Promise。
|
||||
*/
|
||||
post(url, payload, queryParams, additionalHeaders) {
|
||||
return this.request({
|
||||
url,
|
||||
method: "post",
|
||||
payload,
|
||||
queryParams,
|
||||
additionalHeaders,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 发送PUT请求并返回一个解析为响应数据的Promise。
|
||||
*
|
||||
* @param url - 要发送请求的URL。
|
||||
* @param payload - 请求的有效负载数据。
|
||||
* @param queryParams - 要包含在URL中的查询参数。
|
||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||
* @returns 一个解析为响应数据的Promise。
|
||||
*/
|
||||
put(url, payload, queryParams, additionalHeaders) {
|
||||
return this.request({
|
||||
url,
|
||||
method: "put",
|
||||
payload,
|
||||
queryParams,
|
||||
additionalHeaders,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 发送DELETE请求并返回一个解析为响应数据的Promise。
|
||||
*
|
||||
* @param url - 要发送请求的URL。
|
||||
* @param payload - 请求的有效负载数据。
|
||||
* @param queryParams - 要包含在URL中的查询参数。
|
||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||
* @returns 一个解析为响应数据的Promise。
|
||||
*/
|
||||
del(url, payload, queryParams, additionalHeaders) {
|
||||
return this.request({
|
||||
url,
|
||||
method: "delete",
|
||||
payload,
|
||||
queryParams,
|
||||
additionalHeaders,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 发送PATCH请求并返回一个解析为响应数据的Promise。
|
||||
*
|
||||
* @param url - 要发送请求的URL。
|
||||
* @param payload - 请求的有效负载数据。
|
||||
* @param queryParams - 要包含在URL中的查询参数。
|
||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||
* @returns 一个解析为响应数据的Promise。
|
||||
*/
|
||||
patch(url, payload, queryParams, additionalHeaders) {
|
||||
return this.request({
|
||||
url,
|
||||
method: "patch",
|
||||
payload,
|
||||
queryParams,
|
||||
additionalHeaders,
|
||||
});
|
||||
}
|
||||
}
|
||||
class NetTool extends NetToolBase {
|
||||
request({ url, method, queryParams, payload, additionalHeaders, }) {
|
||||
return super.request({
|
||||
url,
|
||||
method,
|
||||
queryParams,
|
||||
payload,
|
||||
additionalHeaders,
|
||||
});
|
||||
}
|
||||
get(url, queryParams, additionalHeaders) {
|
||||
return super.get(url, queryParams, additionalHeaders);
|
||||
}
|
||||
post(url, payload, queryParams, additionalHeaders) {
|
||||
return super.post(url, payload, queryParams, additionalHeaders);
|
||||
}
|
||||
put(url, payload, queryParams, additionalHeaders) {
|
||||
return super.put(url, payload, queryParams, additionalHeaders);
|
||||
}
|
||||
del(url, payload, queryParams, additionalHeaders) {
|
||||
return super.del(url, payload, queryParams, additionalHeaders);
|
||||
}
|
||||
patch(url, payload, queryParams, additionalHeaders) {
|
||||
return super.patch(url, payload, queryParams, additionalHeaders);
|
||||
}
|
||||
/**
|
||||
* 创建一个表示400 Bad Request的响应对象。
|
||||
*
|
||||
* @param message - 错误消息。
|
||||
* @returns 一个表示400 Bad Request的响应对象。
|
||||
*/
|
||||
badRequest(message) {
|
||||
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) {
|
||||
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, data) {
|
||||
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) {
|
||||
this.logger.info(`return a ok response: ${JSON.stringify(data)}`);
|
||||
return Response.json({
|
||||
code: 0,
|
||||
message: "success",
|
||||
data,
|
||||
requestId: this.requestId,
|
||||
});
|
||||
}
|
||||
}
|
||||
export { NetTool, NetToolBase };
|
22
eslint.config.js
Normal file
22
eslint.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
import pluginJs from "@eslint/js"
|
||||
import simpleImportSort from "eslint-plugin-simple-import-sort"
|
||||
import globals from "globals"
|
||||
import tseslint from "typescript-eslint"
|
||||
|
||||
export default [
|
||||
{ files: ["**/*.{js,mjs,cjs,ts}"] },
|
||||
{ languageOptions: { globals: globals.browser } },
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: {
|
||||
"simple-import-sort": simpleImportSort,
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
},
|
||||
},
|
||||
]
|
47
package.json
Normal file
47
package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "egg-tools",
|
||||
"version": "1.0.0",
|
||||
"description": "Tools for Egg projects, including netTool and logger",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "eslint --fix ./src",
|
||||
"prepare": "husky",
|
||||
"prettier": "prettier --write ./src"
|
||||
},
|
||||
"keywords": [
|
||||
"egg",
|
||||
"tools",
|
||||
"logger",
|
||||
"netTool"
|
||||
],
|
||||
"author": "RainSun <zhaoyingbo@live.cn>",
|
||||
"license": "ISC",
|
||||
"lint-staged": {
|
||||
"src/*.{js,jsx,ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.4.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@eslint/js": "^9.9.0",
|
||||
"@types/node": "^22.4.0",
|
||||
"eslint": "^9.9.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"globals": "^15.9.0",
|
||||
"husky": "^9.1.4",
|
||||
"lint-staged": "^15.2.9",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"winston": "^3.14.2",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
}
|
||||
}
|
6
prettier.config.js
Normal file
6
prettier.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
trailingComma: "es5",
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
singleQuote: false,
|
||||
}
|
4
src/index.ts
Normal file
4
src/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import loggerIns from "./logger"
|
||||
import { NetTool, NetToolBase } from "./netTool"
|
||||
|
||||
export { loggerIns, NetTool, NetToolBase }
|
55
src/logger.ts
Normal file
55
src/logger.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import "winston-daily-rotate-file"
|
||||
|
||||
import winston, { format } from "winston"
|
||||
|
||||
const isProd = process.env.NODE_ENV === "production"
|
||||
|
||||
const transports: any[] = [
|
||||
new winston.transports.Console({
|
||||
level: "info",
|
||||
}),
|
||||
]
|
||||
|
||||
if (isProd) {
|
||||
const config = {
|
||||
datePattern: "YYYY-MM-DD",
|
||||
zippedArchive: true,
|
||||
maxSize: "20m",
|
||||
maxFiles: "14d",
|
||||
}
|
||||
|
||||
transports.push(
|
||||
new winston.transports.DailyRotateFile({
|
||||
level: "info",
|
||||
filename: "/home/work/log/egg-info-%DATE%.log",
|
||||
...config,
|
||||
})
|
||||
)
|
||||
transports.push(
|
||||
new winston.transports.DailyRotateFile({
|
||||
level: "debug",
|
||||
filename: "/home/work/log/egg-debug-%DATE%.log",
|
||||
...config,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const loggerIns = winston.createLogger({
|
||||
level: "silly",
|
||||
format: format.combine(
|
||||
format.colorize({
|
||||
level: !isProd,
|
||||
}), // 开发环境下输出彩色日志
|
||||
format.simple(), // 简单文本格式化
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||
format.printf(({ level, message, timestamp, requestId }) => {
|
||||
const singleLineMessage = isProd
|
||||
? message.replace(/\n/g, " ") // 将换行符替换为空格
|
||||
: message
|
||||
return `${timestamp} [${level}]${requestId ? ` [RequestId: ${requestId}]` : ""}: ${singleLineMessage}`
|
||||
})
|
||||
),
|
||||
transports,
|
||||
})
|
||||
|
||||
export default loggerIns
|
422
src/netTool.ts
Normal file
422
src/netTool.ts
Normal file
@ -0,0 +1,422 @@
|
||||
import { Logger } from "winston"
|
||||
|
||||
import loggerIns from "./logger"
|
||||
|
||||
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 { NetTool, NetToolBase }
|
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": false,
|
||||
"noEmit": false,
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"declaration": true,
|
||||
"rootDir": "./src", // 设置 rootDir
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
}
|
1
tsconfig.tsbuildinfo
Normal file
1
tsconfig.tsbuildinfo
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user