koa3-cli 1.0.6 → 1.0.8
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.md +66 -3
- package/app/controller/user.js +29 -6
- package/app/lib/validator.js +70 -0
- package/app/middleware/errorHandler.js +3 -2
- package/app/router.js +1 -0
- package/package.json +2 -1
- package/public/index.html +69 -15
package/README.md
CHANGED
|
@@ -50,9 +50,13 @@ koa3-cli/
|
|
|
50
50
|
│ ├── middleware/ # 中间件目录
|
|
51
51
|
│ │ ├── index.js # 中间件入口
|
|
52
52
|
│ │ ├── auth.js # 认证中间件示例
|
|
53
|
-
│ │
|
|
53
|
+
│ │ ├── requestLogger.js # 请求日志中间件
|
|
54
|
+
│ │ └── errorHandler.js # 全局错误处理
|
|
54
55
|
│ ├── lib/ # 基础能力目录
|
|
55
|
-
│ │
|
|
56
|
+
│ │ ├── logger.js # 日志工具
|
|
57
|
+
│ │ └── validator.js # 参数校验中间件(Joi)
|
|
58
|
+
│ ├── schema/ # 参数校验规则目录
|
|
59
|
+
│ │ └── user.js # 用户相关校验规则
|
|
56
60
|
│ └── router.js # 路由配置
|
|
57
61
|
├── config/ # 配置文件目录
|
|
58
62
|
│ ├── config.default.js # 默认配置
|
|
@@ -74,8 +78,9 @@ koa3-cli/
|
|
|
74
78
|
- ✅ 支持多环境配置(development/production)
|
|
75
79
|
- ✅ MVC 架构(Controller/Service/Model)
|
|
76
80
|
- ✅ 中间件支持
|
|
77
|
-
- ✅
|
|
81
|
+
- ✅ 统一的错误处理(校验失败返回 422,message 为第一条校验提示)
|
|
78
82
|
- ✅ 内置日志系统(访问日志、错误日志、请求追踪)
|
|
83
|
+
- ✅ 基于 Joi 的参数校验(body/query/params,校验结果挂到 `ctx.state.validated`)
|
|
79
84
|
- ✅ RESTful API 示例
|
|
80
85
|
|
|
81
86
|
## 快速开始
|
|
@@ -139,6 +144,57 @@ LOG_ENABLE_FILE=true
|
|
|
139
144
|
- 如果没有,服务端会自动生成并在响应头返回
|
|
140
145
|
- 出错日志会带上 `requestId`,便于串联排查
|
|
141
146
|
|
|
147
|
+
## 参数校验
|
|
148
|
+
|
|
149
|
+
使用 Joi 进行请求参数校验,通过 `app/lib/validator.js` 的 `validate(schemas)` 生成中间件。
|
|
150
|
+
|
|
151
|
+
### 使用方式
|
|
152
|
+
|
|
153
|
+
在路由中挂载校验中间件,按需校验 `body`、`query`、`params`:
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
const { validate } = require('./lib/validator');
|
|
157
|
+
const userSchema = require('./schema/user');
|
|
158
|
+
|
|
159
|
+
// 只校验路径参数
|
|
160
|
+
router.get('/api/user/:id', validate({ params: userSchema.idParam }), userController.detail);
|
|
161
|
+
|
|
162
|
+
// 只校验请求体
|
|
163
|
+
router.post('/api/user', validate({ body: userSchema.createUserBody }), userController.create);
|
|
164
|
+
|
|
165
|
+
// 同时校验 params + body
|
|
166
|
+
router.put('/api/user/:id', validate({
|
|
167
|
+
params: userSchema.idParam,
|
|
168
|
+
body: userSchema.updateUserBody
|
|
169
|
+
}), userController.update);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
校验通过后,结果在 **`ctx.state.validated`** 中:
|
|
173
|
+
|
|
174
|
+
- `ctx.state.validated.body`:校验后的 body
|
|
175
|
+
- `ctx.state.validated.query`:校验后的 query
|
|
176
|
+
- `ctx.state.validated.params`:校验后的 params
|
|
177
|
+
|
|
178
|
+
控制器中应优先使用 `ctx.state.validated`,未走校验的路由可继续使用 `ctx.request.body` / `ctx.params`。
|
|
179
|
+
|
|
180
|
+
### 校验失败响应
|
|
181
|
+
|
|
182
|
+
校验未通过时返回 **422**,`message` 为**第一条**未通过项的提示,`errors` 为全部校验项:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"success": false,
|
|
187
|
+
"message": "用户名为必填",
|
|
188
|
+
"errors": [
|
|
189
|
+
{ "field": "name", "message": "用户名为必填" }
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 添加新的校验规则
|
|
195
|
+
|
|
196
|
+
在 `app/schema/` 下新增或修改 schema 文件,使用 Joi 定义规则(支持 `.messages()` 自定义提示),在路由中通过 `validate({ body: xxx })` 等引用即可。详见 `app/schema/user.js`。
|
|
197
|
+
|
|
142
198
|
## API 示例
|
|
143
199
|
|
|
144
200
|
### 获取用户列表
|
|
@@ -212,11 +268,18 @@ router.get('/api/product', productController.list);
|
|
|
212
268
|
|
|
213
269
|
在 `app/middleware/` 目录下创建中间件文件,然后在 `app/middleware/index.js` 中引入使用。
|
|
214
270
|
|
|
271
|
+
### 添加参数校验
|
|
272
|
+
|
|
273
|
+
1. 在 `app/schema/` 下定义 Joi 规则(可参考 `app/schema/user.js`)
|
|
274
|
+
2. 在 `app/router.js` 中为对应路由添加 `validate({ body, query, params })` 中间件
|
|
275
|
+
3. 在控制器中从 `ctx.state.validated` 读取已校验数据
|
|
276
|
+
|
|
215
277
|
## 技术栈
|
|
216
278
|
|
|
217
279
|
- **Koa3**: Web 框架
|
|
218
280
|
- **@koa/router**: 路由
|
|
219
281
|
- **koa-bodyparser**: 请求体解析
|
|
282
|
+
- **joi**: 参数校验(body/query/params)
|
|
220
283
|
- **koa-static**: 静态资源服务
|
|
221
284
|
- **koa-views**: 模板引擎支持
|
|
222
285
|
- **dotenv**: 环境变量管理
|
package/app/controller/user.js
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
const userService = require('../service/user');
|
|
2
|
+
const { Joi, validateValue } = require('../lib/validator');
|
|
3
|
+
|
|
4
|
+
const idParamSchema = Joi.object({
|
|
5
|
+
id: Joi.string().required().messages({ 'any.required': '用户 id 不能为空' })
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const createUserBodySchema = Joi.object({
|
|
9
|
+
name: Joi.string().trim().min(1).max(100).required().messages({
|
|
10
|
+
'any.required': '用户名为必填',
|
|
11
|
+
'string.empty': '用户名不能为空',
|
|
12
|
+
'string.max': '用户名不能超过 100 个字符'
|
|
13
|
+
}),
|
|
14
|
+
email: Joi.string().email().allow('').optional(),
|
|
15
|
+
age: Joi.number().integer().min(0).max(150).optional()
|
|
16
|
+
}).options({ stripUnknown: true });
|
|
17
|
+
|
|
18
|
+
const updateUserBodySchema = Joi.object({
|
|
19
|
+
name: Joi.string().trim().min(1).max(100).optional(),
|
|
20
|
+
email: Joi.string().email().allow('').optional(),
|
|
21
|
+
age: Joi.number().integer().min(0).max(150).optional()
|
|
22
|
+
}).min(1).messages({
|
|
23
|
+
'object.min': '至少需要提供一个要更新的字段'
|
|
24
|
+
}).options({ stripUnknown: true });
|
|
2
25
|
|
|
3
26
|
function logMeta(ctx, extra = {}) {
|
|
4
27
|
return {
|
|
@@ -22,7 +45,7 @@ class UserController {
|
|
|
22
45
|
}
|
|
23
46
|
|
|
24
47
|
async detail(ctx) {
|
|
25
|
-
const { id } = ctx.params;
|
|
48
|
+
const { id } = await validateValue(idParamSchema, ctx.params);
|
|
26
49
|
|
|
27
50
|
try {
|
|
28
51
|
const user = await userService.getUserById(id);
|
|
@@ -42,7 +65,7 @@ class UserController {
|
|
|
42
65
|
}
|
|
43
66
|
|
|
44
67
|
async create(ctx) {
|
|
45
|
-
const userData = ctx.request.body;
|
|
68
|
+
const userData = await validateValue(createUserBodySchema, ctx.request.body);
|
|
46
69
|
|
|
47
70
|
try {
|
|
48
71
|
const user = await userService.createUser(userData);
|
|
@@ -56,8 +79,8 @@ class UserController {
|
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
async update(ctx) {
|
|
59
|
-
const { id } = ctx.params;
|
|
60
|
-
const userData = ctx.request.body;
|
|
82
|
+
const { id } = await validateValue(idParamSchema, ctx.params);
|
|
83
|
+
const userData = await validateValue(updateUserBodySchema, ctx.request.body);
|
|
61
84
|
|
|
62
85
|
try {
|
|
63
86
|
const user = await userService.updateUser(id, userData);
|
|
@@ -77,7 +100,7 @@ class UserController {
|
|
|
77
100
|
}
|
|
78
101
|
|
|
79
102
|
async delete(ctx) {
|
|
80
|
-
const { id } = ctx.params;
|
|
103
|
+
const { id } = await validateValue(idParamSchema, ctx.params);
|
|
81
104
|
|
|
82
105
|
try {
|
|
83
106
|
const result = await userService.deleteUser(id);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const Joi = require('joi');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 根据 schemas 生成 Koa 参数校验中间件
|
|
5
|
+
* @param {Object} schemas - { body?: Joi.Schema, query?: Joi.Schema, params?: Joi.Schema }
|
|
6
|
+
* @returns {Function} Koa middleware
|
|
7
|
+
*
|
|
8
|
+
* 校验通过后,结果会挂到 ctx.state.validated 上:
|
|
9
|
+
* ctx.state.validated.body / .query / .params
|
|
10
|
+
*/
|
|
11
|
+
function validate(schemas = {}) {
|
|
12
|
+
return async function validateMiddleware(ctx, next) {
|
|
13
|
+
const result = {};
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
if (schemas.body) {
|
|
17
|
+
const value = ctx.request.body ?? {};
|
|
18
|
+
result.body = await schemas.body.validateAsync(value, { stripUnknown: true });
|
|
19
|
+
}
|
|
20
|
+
if (schemas.query) {
|
|
21
|
+
const value = ctx.query ?? {};
|
|
22
|
+
result.query = await schemas.query.validateAsync(value, { stripUnknown: true });
|
|
23
|
+
}
|
|
24
|
+
if (schemas.params) {
|
|
25
|
+
const value = ctx.params ?? {};
|
|
26
|
+
result.params = await schemas.params.validateAsync(value, { stripUnknown: true });
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
if (!Joi.isError(err)) {
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
const validationError = new Error(err.message || 'Validation Failed');
|
|
33
|
+
validationError.status = 422;
|
|
34
|
+
validationError.details = err.details.map(d => ({
|
|
35
|
+
field: d.path.join('.'),
|
|
36
|
+
message: d.message
|
|
37
|
+
}));
|
|
38
|
+
throw validationError;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
ctx.state.validated = result;
|
|
42
|
+
await next();
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 在控制器内校验单个对象,失败时抛出与 validate() 中间件相同形态的 422 错误
|
|
48
|
+
*/
|
|
49
|
+
async function validateValue(schema, value, validateOptions = { stripUnknown: true }) {
|
|
50
|
+
try {
|
|
51
|
+
return await schema.validateAsync(value ?? {}, validateOptions);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (!Joi.isError(err)) {
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
const validationError = new Error(err.message || 'Validation Failed');
|
|
57
|
+
validationError.status = 422;
|
|
58
|
+
validationError.details = err.details.map(d => ({
|
|
59
|
+
field: d.path.join('.'),
|
|
60
|
+
message: d.message
|
|
61
|
+
}));
|
|
62
|
+
throw validationError;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
validate,
|
|
68
|
+
validateValue,
|
|
69
|
+
Joi
|
|
70
|
+
};
|
|
@@ -7,10 +7,11 @@ module.exports = function createErrorHandler(config, logger) {
|
|
|
7
7
|
await next();
|
|
8
8
|
} catch (err) {
|
|
9
9
|
ctx.status = err.status || 500;
|
|
10
|
+
const firstDetailMessage = err.details && err.details[0] && err.details[0].message;
|
|
10
11
|
ctx.body = {
|
|
11
12
|
success: false,
|
|
12
|
-
message: err.message || 'Internal Server Error',
|
|
13
|
-
...(
|
|
13
|
+
message: firstDetailMessage || err.message || 'Internal Server Error',
|
|
14
|
+
...(err.details && { errors: err.details })
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
logger.error('Request failed', {
|
package/app/router.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa3-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Koa3脚手架",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"@ladjs/koa-views": "^9.0.0",
|
|
30
30
|
"dotenv": "^17.3.1",
|
|
31
31
|
"ejs": "^4.0.1",
|
|
32
|
+
"joi": "^17.13.3",
|
|
32
33
|
"koa": "^3.1.2",
|
|
33
34
|
"koa-bodyparser": "^4.4.1",
|
|
34
35
|
"koa-cors": "^0.0.16",
|
package/public/index.html
CHANGED
|
@@ -149,9 +149,13 @@ npm run dev</code></pre>
|
|
|
149
149
|
│ ├── middleware/ # 中间件目录
|
|
150
150
|
│ │ ├── index.js # 中间件入口
|
|
151
151
|
│ │ ├── auth.js # 认证中间件示例
|
|
152
|
-
│ │
|
|
152
|
+
│ │ ├── requestLogger.js # 请求日志中间件
|
|
153
|
+
│ │ └── errorHandler.js # 全局错误处理
|
|
153
154
|
│ ├── lib/ # 基础能力目录
|
|
154
|
-
│ │
|
|
155
|
+
│ │ ├── logger.js # 日志工具
|
|
156
|
+
│ │ └── validator.js # 参数校验中间件(Joi)
|
|
157
|
+
│ ├── schema/ # 参数校验规则目录
|
|
158
|
+
│ │ └── user.js # 用户相关校验规则
|
|
155
159
|
│ ├── router.js # 路由配置
|
|
156
160
|
│ └── view/ # 视图模板目录(可选)
|
|
157
161
|
│ └── docs.ejs # 页面模板
|
|
@@ -422,6 +426,15 @@ module.exports = async (ctx, next) => {
|
|
|
422
426
|
await logger(ctx, next);
|
|
423
427
|
// 其他中间件...
|
|
424
428
|
};</code></pre>
|
|
429
|
+
<h2>添加参数校验</h2>
|
|
430
|
+
<p>使用 Joi 在路由上挂载 <code>validate(schemas)</code>,按需校验 <code>body</code>、<code>query</code>、<code>params</code>。校验通过后从 <code>ctx.state.validated</code> 读取数据。</p>
|
|
431
|
+
<pre><code class="language-javascript">const { validate } = require('./lib/validator');
|
|
432
|
+
const userSchema = require('./schema/user');
|
|
433
|
+
|
|
434
|
+
router.get('/api/user/:id', validate({ params: userSchema.idParam }), userController.detail);
|
|
435
|
+
router.post('/api/user', validate({ body: userSchema.createUserBody }), userController.create);
|
|
436
|
+
router.put('/api/user/:id', validate({ params: userSchema.idParam, body: userSchema.updateUserBody }), userController.update);</code></pre>
|
|
437
|
+
<p>校验规则定义在 <code>app/schema/</code> 下(如 <code>user.js</code>),使用 Joi 编写,支持 <code>.messages()</code> 自定义提示。校验未通过时返回 422,<code>message</code> 为第一条未通过项的提示,<code>errors</code> 为全部校验项。</p>
|
|
425
438
|
<h2>错误处理</h2>
|
|
426
439
|
<p>控制器中的错误会自动被错误处理中间件捕获:</p>
|
|
427
440
|
<pre><code class="language-javascript">// 抛出错误
|
|
@@ -429,11 +442,19 @@ ctx.throw(400, 'Bad Request');
|
|
|
429
442
|
|
|
430
443
|
// 或者
|
|
431
444
|
throw new Error('Something went wrong');</code></pre>
|
|
432
|
-
<p
|
|
445
|
+
<p>错误会被统一处理并返回。普通错误:</p>
|
|
433
446
|
<pre><code class="language-json">{
|
|
434
447
|
"success": false,
|
|
435
448
|
"message": "Error message",
|
|
436
449
|
"stack": "..." // 仅在开发环境显示
|
|
450
|
+
}</code></pre>
|
|
451
|
+
<p>参数校验失败(422)时,<code>message</code> 为第一条校验提示,<code>errors</code> 为所有校验项:</p>
|
|
452
|
+
<pre><code class="language-json">{
|
|
453
|
+
"success": false,
|
|
454
|
+
"message": "用户名为必填",
|
|
455
|
+
"errors": [
|
|
456
|
+
{ "field": "name", "message": "用户名为必填" }
|
|
457
|
+
]
|
|
437
458
|
}</code></pre>
|
|
438
459
|
<h2>响应格式</h2>
|
|
439
460
|
<p>API 请求会自动统一响应格式:</p>
|
|
@@ -469,6 +490,15 @@ throw new Error('Something went wrong');</code></pre>
|
|
|
469
490
|
"success": false,
|
|
470
491
|
"message": "错误信息",
|
|
471
492
|
"stack": "..." // 仅在开发环境显示
|
|
493
|
+
}</code></pre>
|
|
494
|
+
<h3>参数校验失败(422)</h3>
|
|
495
|
+
<p><code>message</code> 为第一条未通过项的提示,<code>errors</code> 为全部校验项:</p>
|
|
496
|
+
<pre><code class="language-json">{
|
|
497
|
+
"success": false,
|
|
498
|
+
"message": "用户名为必填",
|
|
499
|
+
"errors": [
|
|
500
|
+
{ "field": "name", "message": "用户名为必填" }
|
|
501
|
+
]
|
|
472
502
|
}</code></pre>
|
|
473
503
|
<h2>API 列表</h2>
|
|
474
504
|
<ul>
|
|
@@ -503,7 +533,7 @@ throw new Error('Something went wrong');</code></pre>
|
|
|
503
533
|
]
|
|
504
534
|
}</code></pre>
|
|
505
535
|
<h2>获取用户详情</h2>
|
|
506
|
-
<p>根据用户 ID
|
|
536
|
+
<p>根据用户 ID 获取用户详情。路径参数 <code>id</code> 会经过 Joi 校验,缺失或非法时返回 422。</p>
|
|
507
537
|
<h3>请求</h3>
|
|
508
538
|
<pre><code class="language-http">GET /api/user/:id</code></pre>
|
|
509
539
|
<h3>路径参数</h3>
|
|
@@ -512,13 +542,15 @@ throw new Error('Something went wrong');</code></pre>
|
|
|
512
542
|
<tr>
|
|
513
543
|
<th>参数</th>
|
|
514
544
|
<th>类型</th>
|
|
545
|
+
<th>必填</th>
|
|
515
546
|
<th>说明</th>
|
|
516
547
|
</tr>
|
|
517
548
|
</thead>
|
|
518
549
|
<tbody>
|
|
519
550
|
<tr>
|
|
520
551
|
<td>id</td>
|
|
521
|
-
<td>
|
|
552
|
+
<td>string</td>
|
|
553
|
+
<td>是</td>
|
|
522
554
|
<td>用户ID</td>
|
|
523
555
|
</tr>
|
|
524
556
|
</tbody>
|
|
@@ -534,12 +566,19 @@ throw new Error('Something went wrong');</code></pre>
|
|
|
534
566
|
}
|
|
535
567
|
}</code></pre>
|
|
536
568
|
<h3>错误响应</h3>
|
|
569
|
+
<p>404 用户不存在:</p>
|
|
537
570
|
<pre><code class="language-json">{
|
|
538
571
|
"success": false,
|
|
539
572
|
"message": "User not found"
|
|
573
|
+
}</code></pre>
|
|
574
|
+
<p>422 参数校验失败(如 id 为空):</p>
|
|
575
|
+
<pre><code class="language-json">{
|
|
576
|
+
"success": false,
|
|
577
|
+
"message": "用户 id 不能为空",
|
|
578
|
+
"errors": [{ "field": "id", "message": "用户 id 不能为空" }]
|
|
540
579
|
}</code></pre>
|
|
541
580
|
<h2>创建用户</h2>
|
|
542
|
-
<p
|
|
581
|
+
<p>创建新用户。请求体会经过 Joi 校验(<code>name</code> 必填、长度等),未通过返回 422。</p>
|
|
543
582
|
<h3>请求</h3>
|
|
544
583
|
<pre><code class="language-http">POST /api/user
|
|
545
584
|
Content-Type: application/json</code></pre>
|
|
@@ -563,14 +602,20 @@ Content-Type: application/json</code></pre>
|
|
|
563
602
|
<td>name</td>
|
|
564
603
|
<td>string</td>
|
|
565
604
|
<td>是</td>
|
|
566
|
-
<td
|
|
605
|
+
<td>用户姓名(1~100 字符)</td>
|
|
567
606
|
</tr>
|
|
568
607
|
<tr>
|
|
569
608
|
<td>email</td>
|
|
570
609
|
<td>string</td>
|
|
571
|
-
<td
|
|
610
|
+
<td>否</td>
|
|
572
611
|
<td>用户邮箱</td>
|
|
573
612
|
</tr>
|
|
613
|
+
<tr>
|
|
614
|
+
<td>age</td>
|
|
615
|
+
<td>number</td>
|
|
616
|
+
<td>否</td>
|
|
617
|
+
<td>年龄(0~150)</td>
|
|
618
|
+
</tr>
|
|
574
619
|
</tbody>
|
|
575
620
|
</table>
|
|
576
621
|
<h3>响应</h3>
|
|
@@ -582,9 +627,15 @@ Content-Type: application/json</code></pre>
|
|
|
582
627
|
"email": "zhangsan@example.com",
|
|
583
628
|
"createdAt": "2024-01-01T00:00:00.000Z"
|
|
584
629
|
}
|
|
630
|
+
}</code></pre>
|
|
631
|
+
<h3>校验失败(422)</h3>
|
|
632
|
+
<pre><code class="language-json">{
|
|
633
|
+
"success": false,
|
|
634
|
+
"message": "用户名为必填",
|
|
635
|
+
"errors": [{ "field": "name", "message": "用户名为必填" }]
|
|
585
636
|
}</code></pre>
|
|
586
637
|
<h2>更新用户</h2>
|
|
587
|
-
<p
|
|
638
|
+
<p>更新用户信息。路径参数与请求体均会校验,至少需提供一个要更新的字段。</p>
|
|
588
639
|
<h3>请求</h3>
|
|
589
640
|
<pre><code class="language-http">PUT /api/user/:id
|
|
590
641
|
Content-Type: application/json</code></pre>
|
|
@@ -600,16 +651,18 @@ Content-Type: application/json</code></pre>
|
|
|
600
651
|
<tbody>
|
|
601
652
|
<tr>
|
|
602
653
|
<td>id</td>
|
|
603
|
-
<td>
|
|
604
|
-
<td>用户ID
|
|
654
|
+
<td>string</td>
|
|
655
|
+
<td>用户ID(必填)</td>
|
|
605
656
|
</tr>
|
|
606
657
|
</tbody>
|
|
607
658
|
</table>
|
|
608
659
|
<h3>请求体</h3>
|
|
609
660
|
<pre><code class="language-json">{
|
|
610
661
|
"name": "李四",
|
|
611
|
-
"email": "lisi@example.com"
|
|
662
|
+
"email": "lisi@example.com",
|
|
663
|
+
"age": 25
|
|
612
664
|
}</code></pre>
|
|
665
|
+
<p>可选字段:<code>name</code>(1~100 字符)、<code>email</code>、<code>age</code>(0~150),至少填一项。</p>
|
|
613
666
|
<h3>响应</h3>
|
|
614
667
|
<pre><code class="language-json">{
|
|
615
668
|
"success": true,
|
|
@@ -622,7 +675,7 @@ Content-Type: application/json</code></pre>
|
|
|
622
675
|
}
|
|
623
676
|
}</code></pre>
|
|
624
677
|
<h2>删除用户</h2>
|
|
625
|
-
<p
|
|
678
|
+
<p>删除指定用户。路径参数 <code>id</code> 会经过校验。</p>
|
|
626
679
|
<h3>请求</h3>
|
|
627
680
|
<pre><code class="language-http">DELETE /api/user/:id</code></pre>
|
|
628
681
|
<h3>路径参数</h3>
|
|
@@ -637,14 +690,15 @@ Content-Type: application/json</code></pre>
|
|
|
637
690
|
<tbody>
|
|
638
691
|
<tr>
|
|
639
692
|
<td>id</td>
|
|
640
|
-
<td>
|
|
641
|
-
<td>用户ID
|
|
693
|
+
<td>string</td>
|
|
694
|
+
<td>用户ID(必填)</td>
|
|
642
695
|
</tr>
|
|
643
696
|
</tbody>
|
|
644
697
|
</table>
|
|
645
698
|
<h3>响应</h3>
|
|
646
699
|
<pre><code class="language-http">204 No Content</code></pre>
|
|
647
700
|
<h3>错误响应</h3>
|
|
701
|
+
<p>404 用户不存在 / 422 参数校验失败(格式同获取用户详情)。</p>
|
|
648
702
|
<pre><code class="language-json">{
|
|
649
703
|
"success": false,
|
|
650
704
|
"message": "User not found"
|