befly 3.9.36 → 3.9.37
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/docs/api.md +110 -0
- package/package.json +4 -4
- package/plugins/tool.ts +77 -1
package/docs/api.md
CHANGED
|
@@ -282,6 +282,116 @@ befly.tool.No(msg: string, data?: any, other?: Record<string, any>)
|
|
|
282
282
|
}
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
+
### Raw - 原始响应
|
|
286
|
+
|
|
287
|
+
用于第三方回调等需要自定义响应格式的场景,直接返回 `Response` 对象。自动识别数据类型并设置正确的 Content-Type。
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
befly.tool.Raw(ctx: RequestContext, data: Record<string, any> | string, options?: ResponseOptions)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**参数说明:**
|
|
294
|
+
|
|
295
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
296
|
+
| ------- | ----------------------------- | ---- | ------ | ------------------------ |
|
|
297
|
+
| ctx | RequestContext | 是 | - | 请求上下文 |
|
|
298
|
+
| data | Record<string, any> \| string | 是 | - | 响应数据(对象或字符串) |
|
|
299
|
+
| options | ResponseOptions | 否 | {} | 响应选项 |
|
|
300
|
+
|
|
301
|
+
**ResponseOptions 选项:**
|
|
302
|
+
|
|
303
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
304
|
+
| ----------- | ---------------------- | -------- | ---------------------------------------- |
|
|
305
|
+
| status | number | 200 | HTTP 状态码 |
|
|
306
|
+
| contentType | string | 自动判断 | Content-Type,默认根据 data 类型自动判断 |
|
|
307
|
+
| headers | Record<string, string> | {} | 额外的响应头 |
|
|
308
|
+
|
|
309
|
+
**Content-Type 自动判断规则:**
|
|
310
|
+
|
|
311
|
+
| data 类型 | 自动 Content-Type |
|
|
312
|
+
| --------------------- | ----------------- |
|
|
313
|
+
| 对象 | application/json |
|
|
314
|
+
| 字符串(以 `<` 开头) | application/xml |
|
|
315
|
+
| 字符串(其他) | text/plain |
|
|
316
|
+
|
|
317
|
+
**使用示例:**
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// JSON 响应(自动)
|
|
321
|
+
return befly.tool.Raw(ctx, { code: 'SUCCESS', message: '成功' });
|
|
322
|
+
|
|
323
|
+
// 纯文本响应(自动)- 支付宝回调
|
|
324
|
+
return befly.tool.Raw(ctx, 'success');
|
|
325
|
+
|
|
326
|
+
// XML 响应(自动判断)
|
|
327
|
+
return befly.tool.Raw(ctx, '<xml><return_code>SUCCESS</return_code></xml>');
|
|
328
|
+
|
|
329
|
+
// XML 响应(手动指定)
|
|
330
|
+
return befly.tool.Raw(ctx, xmlString, { contentType: 'application/xml' });
|
|
331
|
+
|
|
332
|
+
// 自定义状态码
|
|
333
|
+
return befly.tool.Raw(ctx, { error: 'Not Found' }, { status: 404 });
|
|
334
|
+
|
|
335
|
+
// 自定义响应头
|
|
336
|
+
return befly.tool.Raw(
|
|
337
|
+
ctx,
|
|
338
|
+
{ code: 'SUCCESS' },
|
|
339
|
+
{
|
|
340
|
+
headers: { 'X-Custom-Header': 'value' }
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**完整回调示例:**
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// 微信支付回调
|
|
349
|
+
export default {
|
|
350
|
+
name: '微信支付回调',
|
|
351
|
+
auth: false,
|
|
352
|
+
rawBody: true,
|
|
353
|
+
handler: async (befly, ctx) => {
|
|
354
|
+
if (!befly.weixin) {
|
|
355
|
+
return befly.tool.Raw(ctx, { code: 'SYSTEM_ERROR', message: 'weixin 插件未配置' });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 处理成功
|
|
359
|
+
return befly.tool.Raw(ctx, { code: 'SUCCESS', message: '' });
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// 支付宝回调
|
|
364
|
+
export default {
|
|
365
|
+
name: '支付宝回调',
|
|
366
|
+
auth: false,
|
|
367
|
+
rawBody: true,
|
|
368
|
+
handler: async (befly, ctx) => {
|
|
369
|
+
// 支付宝要求返回纯文本 "success"
|
|
370
|
+
return befly.tool.Raw(ctx, 'success');
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// 微信公众号 XML 回调
|
|
375
|
+
export default {
|
|
376
|
+
name: '微信公众号回调',
|
|
377
|
+
auth: false,
|
|
378
|
+
rawBody: true,
|
|
379
|
+
handler: async (befly, ctx) => {
|
|
380
|
+
// 返回 XML 格式响应
|
|
381
|
+
const xml = `<xml>
|
|
382
|
+
<ToUserName><![CDATA[${fromUser}]]></ToUserName>
|
|
383
|
+
<FromUserName><![CDATA[${toUser}]]></FromUserName>
|
|
384
|
+
<CreateTime>${Date.now()}</CreateTime>
|
|
385
|
+
<MsgType><![CDATA[text]]></MsgType>
|
|
386
|
+
<Content><![CDATA[收到]]></Content>
|
|
387
|
+
</xml>`;
|
|
388
|
+
return befly.tool.Raw(ctx, xml);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**注意**:`Raw` 返回的是 `Response` 对象,会直接作为 HTTP 响应返回,不经过 `FinalResponse` 处理。
|
|
394
|
+
|
|
285
395
|
### ErrorResponse - Hook 中断响应
|
|
286
396
|
|
|
287
397
|
在 Hook 中使用,用于提前拦截请求:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.37",
|
|
4
4
|
"description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -65,16 +65,16 @@
|
|
|
65
65
|
"bun": ">=1.3.0"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"befly-shared": "^1.2.
|
|
68
|
+
"befly-shared": "^1.2.8",
|
|
69
69
|
"chalk": "^5.6.2",
|
|
70
|
-
"es-toolkit": "^1.
|
|
70
|
+
"es-toolkit": "^1.43.0",
|
|
71
71
|
"fast-jwt": "^6.1.0",
|
|
72
72
|
"fast-xml-parser": "^5.3.3",
|
|
73
73
|
"pathe": "^2.0.3",
|
|
74
74
|
"pino": "^10.1.0",
|
|
75
75
|
"pino-roll": "^4.0.0"
|
|
76
76
|
},
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "e078b94f087ddf21f289b55ab86c0b8dce105807",
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"typescript": "^5.9.3"
|
|
80
80
|
}
|
package/plugins/tool.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// 类型导入
|
|
7
7
|
import type { Plugin } from '../types/plugin.js';
|
|
8
|
+
import type { RequestContext } from '../types/context.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* 成功响应
|
|
@@ -38,11 +39,86 @@ export function No(msg: string, data: any = null, other: Record<string, any> = {
|
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* 响应选项
|
|
44
|
+
*/
|
|
45
|
+
interface ResponseOptions {
|
|
46
|
+
/** HTTP 状态码,默认 200 */
|
|
47
|
+
status?: number;
|
|
48
|
+
/** Content-Type,默认根据 data 类型自动判断 */
|
|
49
|
+
contentType?: string;
|
|
50
|
+
/** 额外的响应头 */
|
|
51
|
+
headers?: Record<string, string>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 统一响应函数
|
|
56
|
+
*
|
|
57
|
+
* 自动识别数据类型并设置正确的 Content-Type:
|
|
58
|
+
* - 对象 → application/json
|
|
59
|
+
* - 字符串 → text/plain
|
|
60
|
+
* - 可通过 options.contentType 手动指定
|
|
61
|
+
*
|
|
62
|
+
* @param ctx 请求上下文
|
|
63
|
+
* @param data 响应数据(对象或字符串)
|
|
64
|
+
* @param options 响应选项
|
|
65
|
+
* @returns Response 对象
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // JSON 响应(自动)
|
|
69
|
+
* return Raw(ctx, { code: 'SUCCESS', message: '成功' });
|
|
70
|
+
*
|
|
71
|
+
* // 纯文本响应(自动)
|
|
72
|
+
* return Raw(ctx, 'success');
|
|
73
|
+
*
|
|
74
|
+
* // XML 响应(手动指定)
|
|
75
|
+
* return Raw(ctx, xmlString, { contentType: 'application/xml' });
|
|
76
|
+
*
|
|
77
|
+
* // 自定义状态码和额外头
|
|
78
|
+
* return Raw(ctx, { error: 'Not Found' }, {
|
|
79
|
+
* status: 404,
|
|
80
|
+
* headers: { 'X-Custom': 'value' }
|
|
81
|
+
* });
|
|
82
|
+
*/
|
|
83
|
+
export function Raw(ctx: RequestContext, data: Record<string, any> | string, options: ResponseOptions = {}): Response {
|
|
84
|
+
const { status = 200, contentType, headers = {} } = options;
|
|
85
|
+
|
|
86
|
+
// 自动判断 Content-Type
|
|
87
|
+
let finalContentType = contentType;
|
|
88
|
+
let body: string;
|
|
89
|
+
|
|
90
|
+
if (typeof data === 'string') {
|
|
91
|
+
// 字符串类型
|
|
92
|
+
body = data;
|
|
93
|
+
if (!finalContentType) {
|
|
94
|
+
// 自动判断:XML 或纯文本
|
|
95
|
+
finalContentType = data.trim().startsWith('<') ? 'application/xml' : 'text/plain';
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
// 对象类型,JSON 序列化
|
|
99
|
+
body = JSON.stringify(data);
|
|
100
|
+
finalContentType = finalContentType || 'application/json';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 合并响应头
|
|
104
|
+
const responseHeaders = {
|
|
105
|
+
...ctx.corsHeaders,
|
|
106
|
+
'Content-Type': finalContentType,
|
|
107
|
+
...headers
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return new Response(body, {
|
|
111
|
+
status: status,
|
|
112
|
+
headers: responseHeaders
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
41
116
|
const plugin: Plugin = {
|
|
42
117
|
handler: () => {
|
|
43
118
|
return {
|
|
44
119
|
Yes: Yes,
|
|
45
|
-
No: No
|
|
120
|
+
No: No,
|
|
121
|
+
Raw: Raw
|
|
46
122
|
};
|
|
47
123
|
}
|
|
48
124
|
};
|