json-api-mocker 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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",
|