@aetherframework/middleware 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +88 -0
- package/LICENSE +21 -0
- package/README.md +693 -0
- package/docs/readme/README.md +679 -0
- package/docs/readme/README_zh.md +680 -0
- package/examples/advanced-router-demo.js +119 -0
- package/examples/advanced-server.js +272 -0
- package/examples/basic-server.js +134 -0
- package/examples/benchmark.js +85 -0
- package/examples/router-demo.js +369 -0
- package/index.js +67 -0
- package/package.json +59 -0
- package/src/core/AetherCompiler.js +118 -0
- package/src/core/AetherContext.js +242 -0
- package/src/core/AetherPipeline.js +375 -0
- package/src/core/AetherRouter.js +347 -0
- package/src/core/AetherStore.js +204 -0
- package/src/middleware/body-parser.js +299 -0
- package/src/middleware/compression.js +248 -0
- package/src/middleware/cors.js +162 -0
- package/src/middleware/json.js +214 -0
- package/src/middleware/jwt.js +929 -0
- package/src/middleware/params.js +227 -0
- package/src/middleware/rate-limit.js +167 -0
- package/src/middleware/router.js +36 -0
- package/src/middleware/security.js +116 -0
- package/src/middleware/session.js +167 -0
- package/src/utils/atomic-ops.js +127 -0
- package/src/utils/env-loader.js +128 -0
- package/src/utils/memory-pool.js +93 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @license MIT
|
|
4
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
5
|
+
* SPDX-License-Identifier: MIT
|
|
6
|
+
* @module @aetherframework/middleware/middleware/json.js
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Create JSON parsing middleware for AetherJS
|
|
10
|
+
* @param {Object} options - JSON parser configuration
|
|
11
|
+
* @returns {Function} - JSON parser middleware function
|
|
12
|
+
*/
|
|
13
|
+
function createJsonMiddleware(options = {}) {
|
|
14
|
+
// Load configuration from environment variables
|
|
15
|
+
const envConfig = {
|
|
16
|
+
limit: process.env.BODY_LIMIT_JSON,
|
|
17
|
+
strict: process.env.JSON_STRICT,
|
|
18
|
+
reviver: process.env.JSON_REVIVER,
|
|
19
|
+
enable: process.env.JSON_ENABLE,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Default configuration
|
|
23
|
+
const defaults = {
|
|
24
|
+
enabled: envConfig.enable !== "false",
|
|
25
|
+
limit: parseSize(envConfig.limit || "1mb"),
|
|
26
|
+
strict: envConfig.strict !== "false",
|
|
27
|
+
reviver: envConfig.reviver ? eval(`(${envConfig.reviver})`) : null,
|
|
28
|
+
|
|
29
|
+
// Error handling
|
|
30
|
+
onError: (context, error) => {
|
|
31
|
+
context.setStatus(400).json({
|
|
32
|
+
error: "Bad Request",
|
|
33
|
+
message: "Invalid JSON format",
|
|
34
|
+
details: error.message,
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// Size limit exceeded handler
|
|
39
|
+
onLimitExceeded: (context, limit) => {
|
|
40
|
+
context.setStatus(413).json({
|
|
41
|
+
error: "Payload Too Large",
|
|
42
|
+
message: `JSON payload exceeds ${limit} bytes limit`,
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Parse size string to bytes
|
|
48
|
+
function parseSize(size) {
|
|
49
|
+
const units = {
|
|
50
|
+
b: 1,
|
|
51
|
+
kb: 1024,
|
|
52
|
+
mb: 1024 * 1024,
|
|
53
|
+
gb: 1024 * 1024 * 1024,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// 1. 确保 size 是字符串,并转换为小写以便匹配
|
|
57
|
+
const lowerSize = String(size).toLowerCase();
|
|
58
|
+
|
|
59
|
+
// 2. 执行正则匹配
|
|
60
|
+
const match = lowerSize.match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)$/);
|
|
61
|
+
|
|
62
|
+
if (!match) {
|
|
63
|
+
throw new Error(`Invalid size format: ${size}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 3. 从匹配数组中提取数值部分 (index 1) 和单位部分 (index 2)
|
|
67
|
+
const value = parseFloat(match[1]); // 提取第一个捕获组(数字)
|
|
68
|
+
const unit = match[2]; // 提取第二个捕获组(单位)
|
|
69
|
+
|
|
70
|
+
// 4. 计算并返回字节数
|
|
71
|
+
return value * (units[unit] || 1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Merge with provided options
|
|
75
|
+
const config = { ...defaults, ...options };
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse JSON from request body
|
|
79
|
+
* @param {Object} request - HTTP request object
|
|
80
|
+
* @returns {Promise<Object>} - Parsed JSON object
|
|
81
|
+
*/
|
|
82
|
+
async function parseJson(request) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
const chunks = [];
|
|
85
|
+
let totalLength = 0;
|
|
86
|
+
|
|
87
|
+
request.on("data", (chunk) => {
|
|
88
|
+
totalLength += chunk.length;
|
|
89
|
+
|
|
90
|
+
if (totalLength > config.limit) {
|
|
91
|
+
request.destroy();
|
|
92
|
+
reject(new Error(`JSON payload exceeds ${config.limit} bytes limit`));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
chunks.push(chunk);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
request.on("end", () => {
|
|
100
|
+
try {
|
|
101
|
+
const buffer = Buffer.concat(chunks);
|
|
102
|
+
const text = buffer.toString("utf8");
|
|
103
|
+
|
|
104
|
+
if (config.strict && text.trim() === "") {
|
|
105
|
+
reject(new Error("Empty JSON payload"));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const parsed = config.reviver
|
|
110
|
+
? JSON.parse(text, config.reviver)
|
|
111
|
+
: JSON.parse(text);
|
|
112
|
+
|
|
113
|
+
resolve(parsed);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
reject(error);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
request.on("error", (error) => {
|
|
120
|
+
reject(error);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* JSON middleware function
|
|
127
|
+
* @param {AetherContext} context - AetherJS execution context
|
|
128
|
+
* @param {Function} next - Next middleware function
|
|
129
|
+
*/
|
|
130
|
+
return async function jsonMiddleware(context, next) {
|
|
131
|
+
if (!config.enabled) {
|
|
132
|
+
return typeof next === 'function' ? await next() : undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Skip if not JSON content type
|
|
136
|
+
const contentType = context.getHeader("content-type") || "";
|
|
137
|
+
if (!contentType.includes("application/json")) {
|
|
138
|
+
return typeof next === 'function' ? await next() : undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Skip if no body is expected
|
|
142
|
+
if (context.method === "GET" || context.method === "HEAD") {
|
|
143
|
+
return typeof next === 'function' ? await next() : undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const contentLength = parseInt(context.getHeader("content-length")) || 0;
|
|
147
|
+
|
|
148
|
+
// Skip if no content
|
|
149
|
+
if (contentLength === 0) {
|
|
150
|
+
return typeof next === 'function' ? await next() : undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check size limit
|
|
154
|
+
if (contentLength > config.limit) {
|
|
155
|
+
return config.onLimitExceeded(context, config.limit);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
// Parse JSON body
|
|
160
|
+
const json = await parseJson(context._request);
|
|
161
|
+
|
|
162
|
+
// Store parsed JSON in context
|
|
163
|
+
context.setState("json", json);
|
|
164
|
+
context.setState("body", json);
|
|
165
|
+
|
|
166
|
+
// Add JSON methods to context
|
|
167
|
+
context.jsonBody = json;
|
|
168
|
+
context.getJson = () => json;
|
|
169
|
+
|
|
170
|
+
if (typeof next === 'function') {
|
|
171
|
+
await next();
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error.message.includes("exceeds")) {
|
|
175
|
+
return config.onLimitExceeded(context, config.limit);
|
|
176
|
+
} else {
|
|
177
|
+
return config.onError(context, error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Add utility functions to the middleware
|
|
185
|
+
createJsonMiddleware.parse = function (text, reviver) {
|
|
186
|
+
try {
|
|
187
|
+
return reviver ? JSON.parse(text, reviver) : JSON.parse(text);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new Error(`JSON parse error: ${error.message}`);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
createJsonMiddleware.stringify = function (value, replacer, space) {
|
|
194
|
+
try {
|
|
195
|
+
return JSON.stringify(value, replacer, space);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
throw new Error(`JSON stringify error: ${error.message}`);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
createJsonMiddleware.isValid = function (text) {
|
|
202
|
+
try {
|
|
203
|
+
JSON.parse(text);
|
|
204
|
+
return true;
|
|
205
|
+
} catch {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
createJsonMiddleware.format = function (json, space = 2) {
|
|
211
|
+
return JSON.stringify(json, null, space);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export default createJsonMiddleware;
|