json-api-mocker 1.0.3 → 1.1.0
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/README.ch.md +104 -3
- package/README.md +99 -0
- package/dist/server.d.ts +2 -7
- package/dist/server.js +42 -99
- package/dist/types.d.ts +7 -1
- package/package.json +4 -2
package/README.ch.md
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
一个轻量级且灵活的 Mock 服务器,通过 JSON 配置快速创建 RESTful API。
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<img src="https://img.shields.io/npm/v/json-
|
|
7
|
-
<img src="https://img.shields.io/npm/l/json-
|
|
8
|
-
<img src="https://img.shields.io/npm/dt/json-
|
|
6
|
+
<img src="https://img.shields.io/npm/v/json-api-mocker" alt="npm version" />
|
|
7
|
+
<img src="https://img.shields.io/npm/l/json-api-mocker" alt="license" />
|
|
8
|
+
<img src="https://img.shields.io/npm/dt/json-api-mocker" alt="downloads" />
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
## ✨ 特性
|
|
12
13
|
|
|
13
14
|
- 🚀 通过 JSON 配置快速搭建
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
- 📝 自动数据持久化
|
|
16
17
|
- 🔍 内置分页支持
|
|
17
18
|
- 🛠 可自定义响应结构
|
|
19
|
+
- 💡 集成 Mock.js 实现强大的数据模拟
|
|
18
20
|
- 💡 TypeScript 支持
|
|
19
21
|
|
|
20
22
|
## 📦 安装
|
|
@@ -152,6 +154,105 @@ Mock 服务器已启动:
|
|
|
152
154
|
}
|
|
153
155
|
```
|
|
154
156
|
|
|
157
|
+
### Mock.js 集成
|
|
158
|
+
|
|
159
|
+
你可以使用 Mock.js 模板来生成动态数据:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"path": "/users",
|
|
164
|
+
"methods": {
|
|
165
|
+
"get": {
|
|
166
|
+
"type": "array",
|
|
167
|
+
"mock": {
|
|
168
|
+
"enabled": true,
|
|
169
|
+
"total": 200,
|
|
170
|
+
"template": {
|
|
171
|
+
"id|+1": 1,
|
|
172
|
+
"name": "@cname",
|
|
173
|
+
"email": "@email",
|
|
174
|
+
"age|18-60": 1,
|
|
175
|
+
"address": "@city(true)",
|
|
176
|
+
"avatar": "@image('200x200')",
|
|
177
|
+
"createTime": "@datetime"
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"pagination": {
|
|
181
|
+
"enabled": true,
|
|
182
|
+
"pageSize": 10
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### 可用的 Mock.js 模板
|
|
190
|
+
|
|
191
|
+
- `@cname` - 生成中文姓名
|
|
192
|
+
- `@name` - 生成英文姓名
|
|
193
|
+
- `@email` - 生成邮箱地址
|
|
194
|
+
- `@datetime` - 生成日期时间
|
|
195
|
+
- `@image` - 生成图片链接
|
|
196
|
+
- `@city` - 生成城市名
|
|
197
|
+
- `@id` - 生成随机 ID
|
|
198
|
+
- `@guid` - 生成 GUID
|
|
199
|
+
- `@title` - 生成标题
|
|
200
|
+
- `@paragraph` - 生成段落
|
|
201
|
+
- `|+1` - 自增数字
|
|
202
|
+
|
|
203
|
+
更多 Mock.js 模板请访问 [Mock.js 文档](http://mockjs.com/examples.html)
|
|
204
|
+
|
|
205
|
+
#### Mock.js 使用示例
|
|
206
|
+
|
|
207
|
+
1. 生成用户列表:
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"mock": {
|
|
211
|
+
"enabled": true,
|
|
212
|
+
"total": 100,
|
|
213
|
+
"template": {
|
|
214
|
+
"id|+1": 1,
|
|
215
|
+
"name": "@cname",
|
|
216
|
+
"email": "@email"
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
2. 生成文章列表:
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"mock": {
|
|
226
|
+
"enabled": true,
|
|
227
|
+
"total": 50,
|
|
228
|
+
"template": {
|
|
229
|
+
"id|+1": 1,
|
|
230
|
+
"title": "@ctitle",
|
|
231
|
+
"content": "@cparagraph",
|
|
232
|
+
"author": "@cname",
|
|
233
|
+
"publishDate": "@datetime"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
3. 生成商品列表:
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"mock": {
|
|
243
|
+
"enabled": true,
|
|
244
|
+
"total": 30,
|
|
245
|
+
"template": {
|
|
246
|
+
"id|+1": 1,
|
|
247
|
+
"name": "@ctitle(3, 5)",
|
|
248
|
+
"price|100-1000.2": 1,
|
|
249
|
+
"category": "@pick(['电子产品', '图书', '服装'])",
|
|
250
|
+
"image": "@image('200x200', '#50B347', '#FFF', 'Mock.js')"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
155
256
|
## 🎯 API 示例
|
|
156
257
|
|
|
157
258
|
### 基本的 CRUD 操作
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A lightweight and flexible mock server that uses JSON configuration to quickly c
|
|
|
15
15
|
- 📝 Automatic data persistence
|
|
16
16
|
- 🔍 Built-in pagination support
|
|
17
17
|
- 🛠 Customizable response schemas
|
|
18
|
+
- 💡 Integration with Mock.js for powerful data mocking
|
|
18
19
|
- 💡 TypeScript support
|
|
19
20
|
|
|
20
21
|
## 📦 Installation
|
|
@@ -152,6 +153,104 @@ Each route can support multiple HTTP methods:
|
|
|
152
153
|
}
|
|
153
154
|
```
|
|
154
155
|
|
|
156
|
+
### Mock.js Integration
|
|
157
|
+
|
|
158
|
+
You can use Mock.js templates to generate dynamic data:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"path": "/users",
|
|
163
|
+
"methods": {
|
|
164
|
+
"get": {
|
|
165
|
+
"type": "array",
|
|
166
|
+
"mock": {
|
|
167
|
+
"enabled": true,
|
|
168
|
+
"total": 200,
|
|
169
|
+
"template": {
|
|
170
|
+
"id|+1": 1,
|
|
171
|
+
"name": "@name",
|
|
172
|
+
"email": "@email",
|
|
173
|
+
"age|18-60": 1,
|
|
174
|
+
"address": "@city(true)",
|
|
175
|
+
"avatar": "@image('200x200')",
|
|
176
|
+
"createTime": "@datetime"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"pagination": {
|
|
180
|
+
"enabled": true,
|
|
181
|
+
"pageSize": 10
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Available Mock.js Templates
|
|
189
|
+
|
|
190
|
+
- `@name` - Generate random name
|
|
191
|
+
- `@email` - Generate random email
|
|
192
|
+
- `@datetime` - Generate random datetime
|
|
193
|
+
- `@image` - Generate random image URL
|
|
194
|
+
- `@city` - Generate random city name
|
|
195
|
+
- `@id` - Generate random ID
|
|
196
|
+
- `@guid` - Generate GUID
|
|
197
|
+
- `@title` - Generate random title
|
|
198
|
+
- `@paragraph` - Generate random paragraph
|
|
199
|
+
- `|+1` - Auto increment number
|
|
200
|
+
|
|
201
|
+
For more Mock.js templates, visit [Mock.js Documentation](http://mockjs.com/examples.html)
|
|
202
|
+
|
|
203
|
+
#### Examples with Mock.js
|
|
204
|
+
|
|
205
|
+
1. Generate user list with random data:
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"mock": {
|
|
209
|
+
"enabled": true,
|
|
210
|
+
"total": 100,
|
|
211
|
+
"template": {
|
|
212
|
+
"id|+1": 1,
|
|
213
|
+
"name": "@name",
|
|
214
|
+
"email": "@email"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
2. Generate article list with random content:
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"mock": {
|
|
224
|
+
"enabled": true,
|
|
225
|
+
"total": 50,
|
|
226
|
+
"template": {
|
|
227
|
+
"id|+1": 1,
|
|
228
|
+
"title": "@title",
|
|
229
|
+
"content": "@paragraph",
|
|
230
|
+
"author": "@name",
|
|
231
|
+
"publishDate": "@datetime"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
3. Generate product list with random prices:
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"mock": {
|
|
241
|
+
"enabled": true,
|
|
242
|
+
"total": 30,
|
|
243
|
+
"template": {
|
|
244
|
+
"id|+1": 1,
|
|
245
|
+
"name": "@title(3, 5)",
|
|
246
|
+
"price|100-1000.2": 1,
|
|
247
|
+
"category": "@pick(['Electronics', 'Books', 'Clothing'])",
|
|
248
|
+
"image": "@image('200x200', '#50B347', '#FFF', 'Mock.js')"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
155
254
|
## 🎯 API Examples
|
|
156
255
|
|
|
157
256
|
### Basic CRUD Operations
|
package/dist/server.d.ts
CHANGED
|
@@ -5,14 +5,9 @@ export declare class MockServer {
|
|
|
5
5
|
private configPath;
|
|
6
6
|
constructor(config: Config, configPath?: string);
|
|
7
7
|
private setupMiddleware;
|
|
8
|
+
private generateMockData;
|
|
9
|
+
private handleRequest;
|
|
8
10
|
private setupRoutes;
|
|
9
|
-
private saveConfig;
|
|
10
|
-
private findUserById;
|
|
11
|
-
private getNextUserId;
|
|
12
11
|
private createRoute;
|
|
13
|
-
private handleRequest;
|
|
14
|
-
private handlePostRequest;
|
|
15
|
-
private handlePutRequest;
|
|
16
|
-
private handleDeleteRequest;
|
|
17
12
|
start(): void;
|
|
18
13
|
}
|
package/dist/server.js
CHANGED
|
@@ -4,15 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.MockServer = void 0;
|
|
7
|
+
const mockjs_1 = __importDefault(require("mockjs"));
|
|
7
8
|
const express_1 = __importDefault(require("express"));
|
|
8
9
|
const cors_1 = __importDefault(require("cors"));
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
10
|
class MockServer {
|
|
12
11
|
constructor(config, configPath = 'data.json') {
|
|
13
12
|
this.app = (0, express_1.default)();
|
|
14
13
|
this.config = config;
|
|
15
|
-
this.configPath =
|
|
14
|
+
this.configPath = configPath;
|
|
16
15
|
this.setupMiddleware();
|
|
17
16
|
this.setupRoutes();
|
|
18
17
|
}
|
|
@@ -20,6 +19,43 @@ class MockServer {
|
|
|
20
19
|
this.app.use((0, cors_1.default)());
|
|
21
20
|
this.app.use(express_1.default.json());
|
|
22
21
|
}
|
|
22
|
+
generateMockData(config) {
|
|
23
|
+
try {
|
|
24
|
+
if (config.mock?.enabled && config.mock.template) {
|
|
25
|
+
const { total, template } = config.mock;
|
|
26
|
+
return mockjs_1.default.mock({
|
|
27
|
+
[`data|${total}`]: [template]
|
|
28
|
+
}).data;
|
|
29
|
+
}
|
|
30
|
+
return config.response || [];
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error('生成 Mock 数据时出错:', error);
|
|
34
|
+
return config.response || [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
handleRequest(config) {
|
|
38
|
+
return (req, res) => {
|
|
39
|
+
try {
|
|
40
|
+
console.log(`收到请求: ${req.method} ${req.url}`);
|
|
41
|
+
let responseData = this.generateMockData(config);
|
|
42
|
+
if (config.pagination?.enabled && Array.isArray(responseData)) {
|
|
43
|
+
const page = parseInt(req.query.page) || 1;
|
|
44
|
+
const pageSize = parseInt(req.query.pageSize) || config.pagination.pageSize;
|
|
45
|
+
const startIndex = (page - 1) * pageSize;
|
|
46
|
+
const endIndex = startIndex + pageSize;
|
|
47
|
+
const paginatedData = responseData.slice(startIndex, endIndex);
|
|
48
|
+
res.header('X-Total-Count', responseData.length.toString());
|
|
49
|
+
responseData = paginatedData;
|
|
50
|
+
}
|
|
51
|
+
res.json(responseData);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('处理请求时出错:', error);
|
|
55
|
+
res.status(500).json({ error: '服务器内部错误' });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
23
59
|
setupRoutes() {
|
|
24
60
|
this.config.routes.forEach((route) => {
|
|
25
61
|
Object.entries(route.methods).forEach(([method, methodConfig]) => {
|
|
@@ -27,28 +63,6 @@ class MockServer {
|
|
|
27
63
|
});
|
|
28
64
|
});
|
|
29
65
|
}
|
|
30
|
-
saveConfig() {
|
|
31
|
-
fs_1.default.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
|
|
32
|
-
}
|
|
33
|
-
findUserById(id) {
|
|
34
|
-
const usersRoute = this.config.routes.find(route => route.path === '/users');
|
|
35
|
-
if (!usersRoute)
|
|
36
|
-
return null;
|
|
37
|
-
const getMethod = usersRoute.methods.get;
|
|
38
|
-
if (!getMethod || !Array.isArray(getMethod.response))
|
|
39
|
-
return null;
|
|
40
|
-
return getMethod.response.find(user => user.id === id);
|
|
41
|
-
}
|
|
42
|
-
getNextUserId() {
|
|
43
|
-
const usersRoute = this.config.routes.find(route => route.path === '/users');
|
|
44
|
-
if (!usersRoute)
|
|
45
|
-
return 1;
|
|
46
|
-
const getMethod = usersRoute.methods.get;
|
|
47
|
-
if (!getMethod || !Array.isArray(getMethod.response))
|
|
48
|
-
return 1;
|
|
49
|
-
const maxId = Math.max(...getMethod.response.map(user => user.id));
|
|
50
|
-
return maxId + 1;
|
|
51
|
-
}
|
|
52
66
|
createRoute(path, method, config) {
|
|
53
67
|
const fullPath = `${this.config.server.baseProxy}${path}`;
|
|
54
68
|
console.log(`创建路由: ${method.toUpperCase()} ${fullPath}`);
|
|
@@ -57,87 +71,16 @@ class MockServer {
|
|
|
57
71
|
this.app.get(fullPath, this.handleRequest(config));
|
|
58
72
|
break;
|
|
59
73
|
case 'post':
|
|
60
|
-
this.app.post(fullPath, this.
|
|
74
|
+
this.app.post(fullPath, this.handleRequest(config));
|
|
61
75
|
break;
|
|
62
76
|
case 'put':
|
|
63
|
-
this.app.put(`${fullPath}/:id`, this.
|
|
77
|
+
this.app.put(`${fullPath}/:id`, this.handleRequest(config));
|
|
64
78
|
break;
|
|
65
79
|
case 'delete':
|
|
66
|
-
this.app.delete(`${fullPath}/:id`, this.
|
|
80
|
+
this.app.delete(`${fullPath}/:id`, this.handleRequest(config));
|
|
67
81
|
break;
|
|
68
82
|
}
|
|
69
83
|
}
|
|
70
|
-
handleRequest(config) {
|
|
71
|
-
return (req, res) => {
|
|
72
|
-
console.log(`收到请求: ${req.method} ${req.url}`);
|
|
73
|
-
if (config.pagination?.enabled && config.type === 'array') {
|
|
74
|
-
const page = parseInt(req.query.page) || 1;
|
|
75
|
-
const pageSize = parseInt(req.query.pageSize) || config.pagination.pageSize;
|
|
76
|
-
const startIndex = (page - 1) * pageSize;
|
|
77
|
-
const endIndex = startIndex + pageSize;
|
|
78
|
-
const data = config.response.slice(startIndex, endIndex);
|
|
79
|
-
res.header('X-Total-Count', config.pagination.totalCount.toString());
|
|
80
|
-
res.json(data);
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
res.json(config.response);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
handlePostRequest(config) {
|
|
88
|
-
return (req, res) => {
|
|
89
|
-
console.log(`收到创建请求: ${req.url}`, req.body);
|
|
90
|
-
const usersRoute = this.config.routes.find(route => route.path === '/users');
|
|
91
|
-
if (!usersRoute) {
|
|
92
|
-
return res.status(404).json({ error: '路由未找到' });
|
|
93
|
-
}
|
|
94
|
-
const getMethod = usersRoute.methods.get;
|
|
95
|
-
if (!getMethod || !Array.isArray(getMethod.response)) {
|
|
96
|
-
return res.status(500).json({ error: '数据格式错误' });
|
|
97
|
-
}
|
|
98
|
-
const newUser = {
|
|
99
|
-
id: this.getNextUserId(),
|
|
100
|
-
...req.body
|
|
101
|
-
};
|
|
102
|
-
getMethod.response.push(newUser);
|
|
103
|
-
this.saveConfig();
|
|
104
|
-
res.status(201).json(newUser);
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
handlePutRequest(config) {
|
|
108
|
-
return (req, res) => {
|
|
109
|
-
const userId = parseInt(req.params.id);
|
|
110
|
-
console.log(`收到更新请求: ${req.url}`, req.body);
|
|
111
|
-
const user = this.findUserById(userId);
|
|
112
|
-
if (!user) {
|
|
113
|
-
return res.status(404).json({ error: '用户未找到' });
|
|
114
|
-
}
|
|
115
|
-
Object.assign(user, req.body);
|
|
116
|
-
this.saveConfig();
|
|
117
|
-
res.json(user);
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
handleDeleteRequest(config) {
|
|
121
|
-
return (req, res) => {
|
|
122
|
-
const userId = parseInt(req.params.id);
|
|
123
|
-
console.log(`收到删除请求: ${req.url}`);
|
|
124
|
-
const usersRoute = this.config.routes.find(route => route.path === '/users');
|
|
125
|
-
if (!usersRoute) {
|
|
126
|
-
return res.status(404).json({ error: '路由未找到' });
|
|
127
|
-
}
|
|
128
|
-
const getMethod = usersRoute.methods.get;
|
|
129
|
-
if (!getMethod || !Array.isArray(getMethod.response)) {
|
|
130
|
-
return res.status(500).json({ error: '数据格式错误' });
|
|
131
|
-
}
|
|
132
|
-
const userIndex = getMethod.response.findIndex(user => user.id === userId);
|
|
133
|
-
if (userIndex === -1) {
|
|
134
|
-
return res.status(404).json({ error: '用户未找到' });
|
|
135
|
-
}
|
|
136
|
-
getMethod.response.splice(userIndex, 1);
|
|
137
|
-
this.saveConfig();
|
|
138
|
-
res.json({ success: true, message: '删除成功' });
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
84
|
start() {
|
|
142
85
|
this.app.listen(this.config.server.port, () => {
|
|
143
86
|
console.log(`Mock 服务器已启动:`);
|
package/dist/types.d.ts
CHANGED
|
@@ -7,10 +7,16 @@ export interface PaginationConfig {
|
|
|
7
7
|
pageSize: number;
|
|
8
8
|
totalCount: number;
|
|
9
9
|
}
|
|
10
|
+
export interface MockConfig {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
total: number;
|
|
13
|
+
template: Record<string, any>;
|
|
14
|
+
}
|
|
10
15
|
export interface MethodConfig {
|
|
11
16
|
type?: 'array' | 'object';
|
|
12
17
|
pagination?: PaginationConfig;
|
|
13
|
-
|
|
18
|
+
mock?: MockConfig;
|
|
19
|
+
response?: any;
|
|
14
20
|
requestSchema?: Record<string, string>;
|
|
15
21
|
params?: string[];
|
|
16
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-api-mocker",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "一个基于 JSON 配置的 Mock 服务器",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -41,12 +41,14 @@
|
|
|
41
41
|
"homepage": "https://github.com/Selteve/json-api-mocker#readme",
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"cors": "^2.8.5",
|
|
44
|
-
"express": "^4.17.1"
|
|
44
|
+
"express": "^4.17.1",
|
|
45
|
+
"mockjs": "^1.1.0"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@types/cors": "^2.8.13",
|
|
48
49
|
"@types/express": "^4.17.13",
|
|
49
50
|
"@types/jest": "^27.5.2",
|
|
51
|
+
"@types/mockjs": "^1.0.10",
|
|
50
52
|
"@types/node": "^16.18.0",
|
|
51
53
|
"jest": "^27.5.1",
|
|
52
54
|
"nodemon": "^2.0.22",
|