egg 4.0.0-beta.0 → 4.0.0-beta.11
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 +1 -0
- package/README.zh-CN.md +1 -0
- package/dist/commonjs/app/extend/context.d.ts +154 -0
- package/dist/commonjs/app/extend/context.js +261 -0
- package/dist/commonjs/app/extend/context.types.d.ts +16 -0
- package/dist/commonjs/app/extend/context.types.js +3 -0
- package/dist/commonjs/app/extend/helper.d.ts +37 -0
- package/dist/commonjs/app/extend/helper.js +49 -0
- package/dist/commonjs/app/extend/request.d.ts +128 -0
- package/dist/commonjs/app/extend/request.js +270 -0
- package/dist/commonjs/app/extend/response.d.ts +25 -0
- package/dist/commonjs/app/extend/response.js +37 -0
- package/dist/commonjs/app/middleware/meta.d.ts +2 -3
- package/dist/commonjs/app/middleware/meta.js +1 -1
- package/dist/commonjs/app/middleware/notfound.d.ts +2 -3
- package/dist/commonjs/app/middleware/notfound.js +1 -1
- package/dist/commonjs/app/middleware/site_file.d.ts +5 -3
- package/dist/commonjs/app/middleware/site_file.js +21 -3
- package/dist/commonjs/config/config.default.js +5 -4
- package/dist/commonjs/config/plugin.js +3 -3
- package/dist/commonjs/index.d.ts +3 -1
- package/dist/commonjs/index.js +8 -6
- package/dist/commonjs/lib/application.d.ts +5 -12
- package/dist/commonjs/lib/application.js +9 -20
- package/dist/commonjs/lib/core/base_context_class.d.ts +2 -2
- package/dist/commonjs/lib/core/base_hook_class.d.ts +1 -1
- package/dist/commonjs/lib/core/context_httpclient.d.ts +3 -3
- package/dist/commonjs/lib/core/context_httpclient.js +1 -1
- package/dist/commonjs/lib/core/httpclient.d.ts +2 -3
- package/dist/commonjs/lib/core/httpclient.js +3 -6
- package/dist/commonjs/lib/core/messenger/IMessenger.d.ts +3 -3
- package/dist/commonjs/lib/core/messenger/index.js +2 -2
- package/dist/commonjs/lib/core/messenger/ipc.d.ts +6 -4
- package/dist/commonjs/lib/core/messenger/ipc.js +31 -20
- package/dist/commonjs/lib/core/messenger/local.d.ts +3 -3
- package/dist/commonjs/lib/core/messenger/local.js +14 -11
- package/dist/commonjs/lib/egg.d.ts +25 -16
- package/dist/commonjs/lib/egg.js +44 -22
- package/dist/commonjs/lib/egg.types.d.ts +6 -0
- package/dist/commonjs/lib/egg.types.js +3 -0
- package/dist/commonjs/lib/type.d.ts +3 -5
- package/dist/commonjs/lib/utils.d.ts +2 -0
- package/dist/commonjs/lib/utils.js +21 -0
- package/dist/commonjs/urllib.d.ts +1 -0
- package/dist/commonjs/urllib.js +18 -0
- package/dist/esm/app/extend/context.d.ts +154 -0
- package/dist/esm/app/extend/context.js +255 -0
- package/dist/esm/app/extend/context.types.d.ts +16 -0
- package/dist/esm/app/extend/context.types.js +2 -0
- package/dist/esm/app/extend/helper.d.ts +37 -0
- package/dist/esm/app/extend/helper.js +43 -0
- package/dist/esm/app/extend/request.d.ts +128 -0
- package/dist/esm/app/extend/request.js +264 -0
- package/dist/esm/app/extend/response.d.ts +25 -0
- package/dist/esm/app/extend/response.js +34 -0
- package/dist/esm/app/middleware/meta.d.ts +2 -3
- package/dist/esm/app/middleware/meta.js +1 -1
- package/dist/esm/app/middleware/notfound.d.ts +2 -3
- package/dist/esm/app/middleware/notfound.js +1 -1
- package/dist/esm/app/middleware/site_file.d.ts +5 -3
- package/dist/esm/app/middleware/site_file.js +21 -3
- package/dist/esm/config/config.default.js +5 -4
- package/dist/esm/config/plugin.js +3 -3
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +5 -4
- package/dist/esm/lib/application.d.ts +5 -12
- package/dist/esm/lib/application.js +9 -20
- package/dist/esm/lib/core/base_context_class.d.ts +2 -2
- package/dist/esm/lib/core/base_hook_class.d.ts +1 -1
- package/dist/esm/lib/core/context_httpclient.d.ts +3 -3
- package/dist/esm/lib/core/context_httpclient.js +1 -1
- package/dist/esm/lib/core/httpclient.d.ts +2 -3
- package/dist/esm/lib/core/httpclient.js +2 -2
- package/dist/esm/lib/core/messenger/IMessenger.d.ts +3 -3
- package/dist/esm/lib/core/messenger/index.js +2 -2
- package/dist/esm/lib/core/messenger/ipc.d.ts +6 -4
- package/dist/esm/lib/core/messenger/ipc.js +29 -18
- package/dist/esm/lib/core/messenger/local.d.ts +3 -3
- package/dist/esm/lib/core/messenger/local.js +14 -11
- package/dist/esm/lib/egg.d.ts +25 -16
- package/dist/esm/lib/egg.js +24 -15
- package/dist/esm/lib/egg.types.d.ts +6 -0
- package/dist/esm/lib/egg.types.js +2 -0
- package/dist/esm/lib/type.d.ts +3 -5
- package/dist/esm/lib/utils.d.ts +2 -0
- package/dist/esm/lib/utils.js +14 -0
- package/dist/esm/urllib.d.ts +1 -0
- package/dist/esm/urllib.js +2 -0
- package/dist/package.json +1 -1
- package/package.json +44 -29
- package/src/app/extend/context.ts +303 -0
- package/src/app/extend/context.types.ts +24 -0
- package/src/app/extend/{helper.js → helper.ts} +14 -13
- package/src/app/extend/{request.js → request.ts} +81 -79
- package/src/app/extend/response.ts +36 -0
- package/src/app/middleware/meta.ts +2 -3
- package/src/app/middleware/notfound.ts +2 -3
- package/src/app/middleware/site_file.ts +26 -7
- package/src/config/config.default.ts +4 -3
- package/src/config/plugin.ts +2 -2
- package/src/index.ts +5 -3
- package/src/lib/application.ts +14 -21
- package/src/lib/core/base_context_class.ts +2 -2
- package/src/lib/core/base_hook_class.ts +1 -1
- package/src/lib/core/context_httpclient.ts +3 -3
- package/src/lib/core/httpclient.ts +4 -5
- package/src/lib/core/messenger/IMessenger.ts +3 -3
- package/src/lib/core/messenger/index.ts +1 -1
- package/src/lib/core/messenger/ipc.ts +31 -20
- package/src/lib/core/messenger/local.ts +13 -11
- package/src/lib/egg.ts +60 -28
- package/src/lib/egg.types.ts +6 -0
- package/src/lib/type.ts +6 -6
- package/src/lib/utils.ts +16 -0
- package/src/urllib.ts +1 -0
- package/src/app/extend/context.js +0 -285
- package/src/app/extend/response.js +0 -101
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import delegate from 'delegates';
|
|
2
|
+
import { assign } from 'utility';
|
|
3
|
+
import { now, diff } from 'performance-ms';
|
|
4
|
+
import {
|
|
5
|
+
utils, Context as EggCoreContext, Router,
|
|
6
|
+
type ContextDelegation as EggCoreContextDelegation,
|
|
7
|
+
} from '@eggjs/core';
|
|
8
|
+
import type { Cookies as ContextCookies } from '@eggjs/cookies';
|
|
9
|
+
import { EggLogger } from 'egg-logger';
|
|
10
|
+
import type { Application } from '../../lib/application.js';
|
|
11
|
+
import type {
|
|
12
|
+
HttpClientRequestURL, HttpClientRequestOptions, HttpClient,
|
|
13
|
+
} from '../../lib/core/httpclient.js';
|
|
14
|
+
import type { ContextHttpClient } from '../../lib/core/context_httpclient.js';
|
|
15
|
+
import type { BaseContextClass } from '../../lib//core/base_context_class.js';
|
|
16
|
+
import Request from './request.js';
|
|
17
|
+
import Response from './response.js';
|
|
18
|
+
import type Helper from './helper.js';
|
|
19
|
+
|
|
20
|
+
import './context.types.js';
|
|
21
|
+
|
|
22
|
+
const HELPER = Symbol('ctx helper');
|
|
23
|
+
const LOCALS = Symbol('ctx locals');
|
|
24
|
+
const LOCALS_LIST = Symbol('ctx localsList');
|
|
25
|
+
const COOKIES = Symbol('ctx cookies');
|
|
26
|
+
const CONTEXT_HTTPCLIENT = Symbol('ctx httpclient');
|
|
27
|
+
const CONTEXT_ROUTER = Symbol('ctx router');
|
|
28
|
+
|
|
29
|
+
interface Cookies extends ContextCookies {
|
|
30
|
+
request: any;
|
|
31
|
+
response: any;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default class Context extends EggCoreContext {
|
|
35
|
+
declare app: Application;
|
|
36
|
+
declare request: Request;
|
|
37
|
+
declare service: BaseContextClass;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Request start time
|
|
41
|
+
* @member {Number} Context#starttime
|
|
42
|
+
*/
|
|
43
|
+
starttime: number;
|
|
44
|
+
/**
|
|
45
|
+
* Request start timer using `performance.now()`
|
|
46
|
+
* @member {Number} Context#performanceStarttime
|
|
47
|
+
*/
|
|
48
|
+
performanceStarttime: number;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the current visitor's cookies.
|
|
52
|
+
*/
|
|
53
|
+
get cookies() {
|
|
54
|
+
let cookies = this[COOKIES];
|
|
55
|
+
if (!cookies) {
|
|
56
|
+
this[COOKIES] = cookies = new this.app.ContextCookies(this, this.app.keys, this.app.config.cookies);
|
|
57
|
+
}
|
|
58
|
+
return cookies as Cookies;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get a wrapper httpclient instance contain ctx in the hold request process
|
|
63
|
+
*
|
|
64
|
+
* @return {ContextHttpClient} the wrapper httpclient instance
|
|
65
|
+
*/
|
|
66
|
+
get httpclient(): ContextHttpClient {
|
|
67
|
+
if (!this[CONTEXT_HTTPCLIENT]) {
|
|
68
|
+
this[CONTEXT_HTTPCLIENT] = new this.app.ContextHttpClient(this as any);
|
|
69
|
+
}
|
|
70
|
+
return this[CONTEXT_HTTPCLIENT] as ContextHttpClient;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Alias to {@link Context#httpclient}
|
|
75
|
+
*/
|
|
76
|
+
get httpClient(): ContextHttpClient {
|
|
77
|
+
return this.httpclient;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Shortcut for httpclient.curl
|
|
82
|
+
*
|
|
83
|
+
* @function Context#curl
|
|
84
|
+
* @param {String|Object} url - request url address.
|
|
85
|
+
* @param {Object} [options] - options for request.
|
|
86
|
+
* @return {Object} see {@link ContextHttpClient#curl}
|
|
87
|
+
*/
|
|
88
|
+
async curl(url: HttpClientRequestURL, options?: HttpClientRequestOptions): ReturnType<HttpClient['request']> {
|
|
89
|
+
return await this.httpclient.curl(url, options);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Alias to {@link Application#router}
|
|
94
|
+
*
|
|
95
|
+
* @member {Router} Context#router
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
* @example
|
|
98
|
+
* ```js
|
|
99
|
+
* this.router.pathFor('post', { id: 12 });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
get router(): Router {
|
|
103
|
+
if (this[CONTEXT_ROUTER]) {
|
|
104
|
+
return this[CONTEXT_ROUTER] as Router;
|
|
105
|
+
}
|
|
106
|
+
return this.app.router;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Set router to Context, only use on EggRouter
|
|
111
|
+
* @param {Router} val router instance
|
|
112
|
+
*/
|
|
113
|
+
set router(val: Router) {
|
|
114
|
+
this[CONTEXT_ROUTER] = val;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get helper instance from {@link Application#Helper}
|
|
119
|
+
*
|
|
120
|
+
* @member {Helper} Context#helper
|
|
121
|
+
* @since 1.0.0
|
|
122
|
+
*/
|
|
123
|
+
get helper(): Helper {
|
|
124
|
+
if (!this[HELPER]) {
|
|
125
|
+
this[HELPER] = new this.app.Helper(this as any);
|
|
126
|
+
}
|
|
127
|
+
return this[HELPER] as Helper;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Wrap app.loggers with context information,
|
|
132
|
+
* if a custom logger is defined by naming aLogger, then you can `ctx.getLogger('aLogger')`
|
|
133
|
+
*
|
|
134
|
+
* @param {String} name - logger name
|
|
135
|
+
*/
|
|
136
|
+
getLogger(name: string): EggLogger {
|
|
137
|
+
return this.app.getLogger(name);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Logger for Application
|
|
142
|
+
*
|
|
143
|
+
* @member {Logger} Context#logger
|
|
144
|
+
* @since 1.0.0
|
|
145
|
+
* @example
|
|
146
|
+
* ```js
|
|
147
|
+
* this.logger.info('some request data: %j', this.request.body);
|
|
148
|
+
* this.logger.warn('WARNING!!!!');
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
get logger(): EggLogger {
|
|
152
|
+
return this.getLogger('logger');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Logger for frameworks and plugins
|
|
157
|
+
*
|
|
158
|
+
* @member {Logger} Context#coreLogger
|
|
159
|
+
* @since 1.0.0
|
|
160
|
+
*/
|
|
161
|
+
get coreLogger(): EggLogger {
|
|
162
|
+
return this.getLogger('coreLogger');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* locals is an object for view, you can use `app.locals` and `ctx.locals` to set variables,
|
|
167
|
+
* which will be used as data when view is rendering.
|
|
168
|
+
* The difference between `app.locals` and `ctx.locals` is the context level, `app.locals` is global level, and `ctx.locals` is request level. when you get `ctx.locals`, it will merge `app.locals`.
|
|
169
|
+
*
|
|
170
|
+
* when you set locals, only object is available
|
|
171
|
+
*
|
|
172
|
+
* ```js
|
|
173
|
+
* this.locals = {
|
|
174
|
+
* a: 1
|
|
175
|
+
* };
|
|
176
|
+
* this.locals = {
|
|
177
|
+
* b: 1
|
|
178
|
+
* };
|
|
179
|
+
* this.locals.c = 1;
|
|
180
|
+
* console.log(this.locals);
|
|
181
|
+
* {
|
|
182
|
+
* a: 1,
|
|
183
|
+
* b: 1,
|
|
184
|
+
* c: 1,
|
|
185
|
+
* };
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* `ctx.locals` has cache, it only merges `app.locals` once in one request.
|
|
189
|
+
*
|
|
190
|
+
* @member {Object} Context#locals
|
|
191
|
+
*/
|
|
192
|
+
get locals() {
|
|
193
|
+
if (!this[LOCALS]) {
|
|
194
|
+
this[LOCALS] = assign({}, this.app.locals);
|
|
195
|
+
}
|
|
196
|
+
if (Array.isArray(this[LOCALS_LIST]) && this[LOCALS_LIST].length > 0) {
|
|
197
|
+
assign(this[LOCALS], this[LOCALS_LIST]);
|
|
198
|
+
this[LOCALS_LIST] = null;
|
|
199
|
+
}
|
|
200
|
+
return this[LOCALS] as Record<string, any>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
set locals(val) {
|
|
204
|
+
const localsList = this[LOCALS_LIST] as Record<string, any>[] ?? [];
|
|
205
|
+
localsList.push(val);
|
|
206
|
+
this[LOCALS_LIST] = localsList;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* alias to {@link Context#locals}, compatible with koa that use this variable
|
|
211
|
+
* @member {Object} state
|
|
212
|
+
* @see Context#locals
|
|
213
|
+
*/
|
|
214
|
+
get state() {
|
|
215
|
+
return this.locals;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
set state(val) {
|
|
219
|
+
this.locals = val;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Run async function in the background
|
|
224
|
+
* @param {Function} scope - the first args is ctx
|
|
225
|
+
* ```js
|
|
226
|
+
* this.body = 'hi';
|
|
227
|
+
*
|
|
228
|
+
* this.runInBackground(async ctx => {
|
|
229
|
+
* await ctx.mysql.query(sql);
|
|
230
|
+
* await ctx.curl(url);
|
|
231
|
+
* });
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
runInBackground(scope: (ctx: ContextDelegation) => Promise<void>, taskName?: string): void {
|
|
235
|
+
// try to use custom function name first
|
|
236
|
+
if (!taskName) {
|
|
237
|
+
taskName = Reflect.get(scope, '_name') || scope.name || utils.getCalleeFromStack(true);
|
|
238
|
+
}
|
|
239
|
+
// use setImmediate to ensure all sync logic will run async
|
|
240
|
+
setImmediate(() => {
|
|
241
|
+
this._runInBackground(scope, taskName!);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// let plugins or frameworks to reuse _runInBackground in some cases.
|
|
246
|
+
// e.g.: https://github.com/eggjs/egg-mock/pull/78
|
|
247
|
+
async _runInBackground(scope: (ctx: ContextDelegation) => Promise<void>, taskName: string) {
|
|
248
|
+
const startTime = now();
|
|
249
|
+
try {
|
|
250
|
+
await scope(this as any);
|
|
251
|
+
this.coreLogger.info('[egg:background] task:%s success (%dms)', taskName, diff(startTime));
|
|
252
|
+
} catch (err: any) {
|
|
253
|
+
// background task process log
|
|
254
|
+
this.coreLogger.info('[egg:background] task:%s fail (%dms)', taskName, diff(startTime));
|
|
255
|
+
|
|
256
|
+
// emit error when promise catch, and set err.runInBackground flag
|
|
257
|
+
err.runInBackground = true;
|
|
258
|
+
this.app.emit('error', err, this);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Context delegation.
|
|
265
|
+
*/
|
|
266
|
+
|
|
267
|
+
delegate(Context.prototype, 'request')
|
|
268
|
+
/**
|
|
269
|
+
* @member {Boolean} Context#acceptJSON
|
|
270
|
+
* @see Request#acceptJSON
|
|
271
|
+
* @since 1.0.0
|
|
272
|
+
*/
|
|
273
|
+
.getter('acceptJSON')
|
|
274
|
+
/**
|
|
275
|
+
* @member {Array} Context#queries
|
|
276
|
+
* @see Request#queries
|
|
277
|
+
* @since 1.0.0
|
|
278
|
+
*/
|
|
279
|
+
.getter('queries')
|
|
280
|
+
/**
|
|
281
|
+
* @member {Boolean} Context#accept
|
|
282
|
+
* @see Request#accept
|
|
283
|
+
* @since 1.0.0
|
|
284
|
+
*/
|
|
285
|
+
.getter('accept')
|
|
286
|
+
/**
|
|
287
|
+
* @member {string} Context#ip
|
|
288
|
+
* @see Request#ip
|
|
289
|
+
* @since 1.0.0
|
|
290
|
+
*/
|
|
291
|
+
.access('ip');
|
|
292
|
+
|
|
293
|
+
delegate(Context.prototype, 'response')
|
|
294
|
+
/**
|
|
295
|
+
* @member {Number} Context#realStatus
|
|
296
|
+
* @see Response#realStatus
|
|
297
|
+
* @since 1.0.0
|
|
298
|
+
*/
|
|
299
|
+
.access('realStatus');
|
|
300
|
+
|
|
301
|
+
export type ContextDelegation = EggCoreContextDelegation & Context
|
|
302
|
+
& Pick<Request, 'acceptJSON' | 'queries' | 'accept' | 'ip'>
|
|
303
|
+
& Pick<Response, 'realStatus'>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Router,
|
|
3
|
+
} from '@eggjs/core';
|
|
4
|
+
import type {
|
|
5
|
+
HttpClientRequestURL, HttpClientRequestOptions, HttpClient,
|
|
6
|
+
} from '../../lib/core/httpclient.js';
|
|
7
|
+
import type {
|
|
8
|
+
ContextHttpClient,
|
|
9
|
+
} from '../../lib/core/context_httpclient.js';
|
|
10
|
+
import type Helper from './helper.js';
|
|
11
|
+
import type { EggLogger } from 'egg-logger';
|
|
12
|
+
|
|
13
|
+
declare module '@eggjs/core' {
|
|
14
|
+
// add Context overrides types
|
|
15
|
+
interface Context {
|
|
16
|
+
curl(url: HttpClientRequestURL, options?: HttpClientRequestOptions): ReturnType<HttpClient['request']>;
|
|
17
|
+
get router(): Router;
|
|
18
|
+
set router(val: Router);
|
|
19
|
+
get helper(): Helper;
|
|
20
|
+
get httpclient(): ContextHttpClient;
|
|
21
|
+
get httpClient(): ContextHttpClient;
|
|
22
|
+
getLogger(name: string): EggLogger;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const url = require('url');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
1
|
+
import url from 'node:url';
|
|
2
|
+
import { BaseContextClass } from '../../lib/core/base_context_class.js';
|
|
7
3
|
|
|
4
|
+
/**
|
|
5
|
+
* The Helper class which can be used as utility function.
|
|
6
|
+
* We support developers to extend Helper through ${baseDir}/app/extend/helper.js ,
|
|
7
|
+
* then you can use all method on `ctx.helper` that is a instance of Helper.
|
|
8
|
+
*/
|
|
9
|
+
export default class Helper extends BaseContextClass {
|
|
8
10
|
/**
|
|
9
11
|
* Generate URL path(without host) for route. Takes the route name and a map of named params.
|
|
10
12
|
* @function Helper#pathFor
|
|
@@ -19,9 +21,9 @@ module.exports = {
|
|
|
19
21
|
* ```
|
|
20
22
|
* @return {String} url path(without host)
|
|
21
23
|
*/
|
|
22
|
-
pathFor(name, params) {
|
|
24
|
+
pathFor(name: string, params: Record<string, any>): string {
|
|
23
25
|
return this.app.router.url(name, params);
|
|
24
|
-
}
|
|
26
|
+
}
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Generate full URL(with host) for route. Takes the route name and a map of named params.
|
|
@@ -36,8 +38,7 @@ module.exports = {
|
|
|
36
38
|
* ```
|
|
37
39
|
* @return {String} full url(with host)
|
|
38
40
|
*/
|
|
39
|
-
urlFor(name, params) {
|
|
40
|
-
return this.ctx.protocol + '://' + this.ctx.host + url.resolve('/', this.
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
};
|
|
41
|
+
urlFor(name: string, params: Record<string, any>): string {
|
|
42
|
+
return this.ctx.protocol + '://' + this.ctx.host + url.resolve('/', this.pathFor(name, params));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import querystring from 'node:querystring';
|
|
2
|
+
import { Request as EggCoreRequest } from '@eggjs/core';
|
|
3
|
+
import type { Application } from '../../lib/application.js';
|
|
4
|
+
import type { ContextDelegation } from './context.js';
|
|
5
|
+
import Response from './response.js';
|
|
2
6
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const PROTOCOL = Symbol('PROTOCOL');
|
|
9
|
-
const HOST = Symbol('HOST');
|
|
10
|
-
const ACCEPTS = Symbol('ACCEPTS');
|
|
11
|
-
const IPS = Symbol('IPS');
|
|
7
|
+
const QUERY_CACHE = Symbol('request query cache');
|
|
8
|
+
const QUERIES_CACHE = Symbol('request queries cache');
|
|
9
|
+
const PROTOCOL = Symbol('request protocol');
|
|
10
|
+
const HOST = Symbol('request host');
|
|
11
|
+
const IPS = Symbol('request ips');
|
|
12
12
|
const RE_ARRAY_KEY = /[^\[\]]+\[\]$/;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
export default class Request extends EggCoreRequest {
|
|
15
|
+
declare app: Application;
|
|
16
|
+
declare ctx: ContextDelegation;
|
|
17
|
+
declare response: Response;
|
|
18
|
+
|
|
15
19
|
/**
|
|
16
20
|
* Parse the "Host" header field host
|
|
17
21
|
* and support X-Forwarded-Host when a
|
|
@@ -29,17 +33,19 @@ module.exports = {
|
|
|
29
33
|
* => 'demo.eggjs.org'
|
|
30
34
|
* ```
|
|
31
35
|
*/
|
|
32
|
-
get host() {
|
|
33
|
-
|
|
36
|
+
get host(): string {
|
|
37
|
+
let host = this[HOST] as string | undefined;
|
|
38
|
+
if (host) {
|
|
39
|
+
return host;
|
|
40
|
+
}
|
|
34
41
|
|
|
35
|
-
let host;
|
|
36
42
|
if (this.app.config.proxy) {
|
|
37
43
|
host = getFromHeaders(this, this.app.config.hostHeaders);
|
|
38
44
|
}
|
|
39
45
|
host = host || this.get('host') || '';
|
|
40
|
-
this[HOST] = host.split(
|
|
41
|
-
return
|
|
42
|
-
}
|
|
46
|
+
this[HOST] = host = host.split(',')[0].trim();
|
|
47
|
+
return host;
|
|
48
|
+
}
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* @member {String} Request#protocol
|
|
@@ -49,25 +55,28 @@ module.exports = {
|
|
|
49
55
|
* => 'https'
|
|
50
56
|
* ```
|
|
51
57
|
*/
|
|
52
|
-
get protocol() {
|
|
53
|
-
|
|
58
|
+
get protocol(): string {
|
|
59
|
+
let protocol = this[PROTOCOL] as string;
|
|
60
|
+
if (protocol) {
|
|
61
|
+
return protocol;
|
|
62
|
+
}
|
|
54
63
|
// detect encrypted socket
|
|
55
|
-
if (this.socket
|
|
56
|
-
this[PROTOCOL] = 'https';
|
|
57
|
-
return
|
|
64
|
+
if (this.socket?.encrypted) {
|
|
65
|
+
this[PROTOCOL] = protocol = 'https';
|
|
66
|
+
return protocol;
|
|
58
67
|
}
|
|
59
68
|
// get from headers specified in `app.config.protocolHeaders`
|
|
60
69
|
if (this.app.config.proxy) {
|
|
61
70
|
const proto = getFromHeaders(this, this.app.config.protocolHeaders);
|
|
62
71
|
if (proto) {
|
|
63
|
-
this[PROTOCOL] = proto.split(/\s*,\s*/)[0];
|
|
64
|
-
return
|
|
72
|
+
this[PROTOCOL] = protocol = proto.split(/\s*,\s*/)[0];
|
|
73
|
+
return protocol;
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
|
-
// use protocol specified in `app.
|
|
68
|
-
this[PROTOCOL] = this.app.config.protocol || 'http';
|
|
69
|
-
return
|
|
70
|
-
}
|
|
76
|
+
// use protocol specified in `app.config.protocol`
|
|
77
|
+
this[PROTOCOL] = protocol = this.app.config.protocol || 'http';
|
|
78
|
+
return protocol;
|
|
79
|
+
}
|
|
71
80
|
|
|
72
81
|
/**
|
|
73
82
|
* Get all pass through ip addresses from the request.
|
|
@@ -80,29 +89,34 @@ module.exports = {
|
|
|
80
89
|
* => ['100.23.1.2', '201.10.10.2']
|
|
81
90
|
* ```
|
|
82
91
|
*/
|
|
83
|
-
get ips() {
|
|
84
|
-
|
|
92
|
+
get ips(): string[] {
|
|
93
|
+
let ips = this[IPS] as string[] | undefined;
|
|
94
|
+
if (ips) {
|
|
95
|
+
return ips;
|
|
96
|
+
}
|
|
85
97
|
|
|
86
98
|
// return empty array when proxy=false
|
|
87
99
|
if (!this.app.config.proxy) {
|
|
88
|
-
this[IPS] = [];
|
|
89
|
-
return
|
|
100
|
+
this[IPS] = ips = [];
|
|
101
|
+
return ips;
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
const val = getFromHeaders(this, this.app.config.ipHeaders)
|
|
93
|
-
this[IPS] = val ? val.split(/\s*,\s*/) : [];
|
|
104
|
+
const val = getFromHeaders(this, this.app.config.ipHeaders);
|
|
105
|
+
this[IPS] = ips = val ? val.split(/\s*,\s*/) : [];
|
|
94
106
|
|
|
95
107
|
let maxIpsCount = this.app.config.maxIpsCount;
|
|
96
108
|
// Compatible with maxProxyCount logic (previous logic is wrong, only for compatibility with legacy logic)
|
|
97
|
-
if (!maxIpsCount && this.app.config.maxProxyCount)
|
|
109
|
+
if (!maxIpsCount && this.app.config.maxProxyCount) {
|
|
110
|
+
maxIpsCount = this.app.config.maxProxyCount + 1;
|
|
111
|
+
}
|
|
98
112
|
|
|
99
113
|
if (maxIpsCount > 0) {
|
|
100
114
|
// if maxIpsCount present, only keep `maxIpsCount` ips
|
|
101
115
|
// [ illegalIp, clientRealIp, proxyIp1, proxyIp2 ...]
|
|
102
|
-
this[IPS] =
|
|
116
|
+
this[IPS] = ips = ips.slice(-maxIpsCount);
|
|
103
117
|
}
|
|
104
|
-
return
|
|
105
|
-
}
|
|
118
|
+
return ips;
|
|
119
|
+
}
|
|
106
120
|
|
|
107
121
|
/**
|
|
108
122
|
* Get the request remote IPv4 address
|
|
@@ -115,16 +129,16 @@ module.exports = {
|
|
|
115
129
|
* => '111.10.2.1'
|
|
116
130
|
* ```
|
|
117
131
|
*/
|
|
118
|
-
get ip() {
|
|
132
|
+
get ip(): string {
|
|
119
133
|
if (this._ip) {
|
|
120
134
|
return this._ip;
|
|
121
135
|
}
|
|
122
|
-
const ip = this.ips[0]
|
|
136
|
+
const ip = this.ips[0] ?? this.socket.remoteAddress;
|
|
123
137
|
// will be '::ffff:x.x.x.x', should convert to standard IPv4 format
|
|
124
138
|
// https://zh.wikipedia.org/wiki/IPv6
|
|
125
|
-
this._ip = ip && ip.
|
|
139
|
+
this._ip = ip && ip.startsWith('::ffff:') ? ip.substring(7) : ip;
|
|
126
140
|
return this._ip;
|
|
127
|
-
}
|
|
141
|
+
}
|
|
128
142
|
|
|
129
143
|
/**
|
|
130
144
|
* Set the request remote IPv4 address
|
|
@@ -137,9 +151,9 @@ module.exports = {
|
|
|
137
151
|
* => '111.10.2.1'
|
|
138
152
|
* ```
|
|
139
153
|
*/
|
|
140
|
-
set ip(ip) {
|
|
154
|
+
set ip(ip: string) {
|
|
141
155
|
this._ip = ip;
|
|
142
|
-
}
|
|
156
|
+
}
|
|
143
157
|
|
|
144
158
|
/**
|
|
145
159
|
* detect if response should be json
|
|
@@ -150,25 +164,25 @@ module.exports = {
|
|
|
150
164
|
* @member {Boolean} Request#acceptJSON
|
|
151
165
|
* @since 1.0.0
|
|
152
166
|
*/
|
|
153
|
-
get acceptJSON() {
|
|
167
|
+
get acceptJSON(): boolean {
|
|
154
168
|
if (this.path.endsWith('.json')) return true;
|
|
155
169
|
if (this.response.type && this.response.type.indexOf('json') >= 0) return true;
|
|
156
170
|
if (this.accepts('html', 'text', 'json') === 'json') return true;
|
|
157
171
|
return false;
|
|
158
|
-
}
|
|
172
|
+
}
|
|
159
173
|
|
|
160
174
|
// How to read query safely
|
|
161
175
|
// https://github.com/koajs/qs/issues/5
|
|
162
|
-
_customQuery(cacheName, filter) {
|
|
176
|
+
_customQuery(cacheName: symbol, filter: (value: string | string[]) => string | string[]) {
|
|
163
177
|
const str = this.querystring || '';
|
|
164
|
-
let c = this[cacheName]
|
|
178
|
+
let c = this[cacheName] as Record<string, Record<string, string | string[]>>;
|
|
165
179
|
if (!c) {
|
|
166
180
|
c = this[cacheName] = {};
|
|
167
181
|
}
|
|
168
182
|
let cacheQuery = c[str];
|
|
169
183
|
if (!cacheQuery) {
|
|
170
184
|
cacheQuery = c[str] = {};
|
|
171
|
-
const isQueries = cacheName ===
|
|
185
|
+
const isQueries = cacheName === QUERIES_CACHE;
|
|
172
186
|
// `querystring.parse` CANNOT parse something like `a[foo]=1&a[bar]=2`
|
|
173
187
|
const query = str ? querystring.parse(str) : {};
|
|
174
188
|
for (const key in query) {
|
|
@@ -176,20 +190,19 @@ module.exports = {
|
|
|
176
190
|
// key is '', like `a=b&`
|
|
177
191
|
continue;
|
|
178
192
|
}
|
|
179
|
-
const value = filter(query[key]);
|
|
193
|
+
const value = filter(query[key]!);
|
|
180
194
|
cacheQuery[key] = value;
|
|
181
195
|
if (isQueries && RE_ARRAY_KEY.test(key)) {
|
|
182
196
|
// `this.queries['key'] => this.queries['key[]']` is compatibly supported
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
cacheQuery[subkey] = value;
|
|
197
|
+
const subKey = key.substring(0, key.length - 2);
|
|
198
|
+
if (!cacheQuery[subKey]) {
|
|
199
|
+
cacheQuery[subKey] = value;
|
|
187
200
|
}
|
|
188
201
|
}
|
|
189
202
|
}
|
|
190
203
|
}
|
|
191
204
|
return cacheQuery;
|
|
192
|
-
}
|
|
205
|
+
}
|
|
193
206
|
|
|
194
207
|
/**
|
|
195
208
|
* get params pass by querystring, all values are of string type.
|
|
@@ -212,8 +225,8 @@ module.exports = {
|
|
|
212
225
|
* ```
|
|
213
226
|
*/
|
|
214
227
|
get query() {
|
|
215
|
-
return this._customQuery(
|
|
216
|
-
}
|
|
228
|
+
return this._customQuery(QUERY_CACHE, firstValue) as Record<string, string>;
|
|
229
|
+
}
|
|
217
230
|
|
|
218
231
|
/**
|
|
219
232
|
* get params pass by querystring, all value are Array type. {@link Request#query}
|
|
@@ -232,50 +245,39 @@ module.exports = {
|
|
|
232
245
|
* ```
|
|
233
246
|
*/
|
|
234
247
|
get queries() {
|
|
235
|
-
return this._customQuery(
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
get accept() {
|
|
239
|
-
let accept = this[ACCEPTS];
|
|
240
|
-
if (accept) {
|
|
241
|
-
return accept;
|
|
242
|
-
}
|
|
243
|
-
accept = this[ACCEPTS] = accepts(this.req);
|
|
244
|
-
return accept;
|
|
245
|
-
},
|
|
248
|
+
return this._customQuery(QUERIES_CACHE, arrayValue) as Record<string, string[]>;
|
|
249
|
+
}
|
|
246
250
|
|
|
247
251
|
/**
|
|
248
252
|
* Set query-string as an object.
|
|
249
253
|
*
|
|
250
254
|
* @function Request#query
|
|
251
255
|
* @param {Object} obj set querystring and query object for request.
|
|
252
|
-
* @return {void}
|
|
253
256
|
*/
|
|
254
|
-
set query(obj) {
|
|
257
|
+
set query(obj: Record<string, string>) {
|
|
255
258
|
this.querystring = querystring.stringify(obj);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
+
}
|
|
260
|
+
}
|
|
259
261
|
|
|
260
|
-
function firstValue(value) {
|
|
262
|
+
function firstValue(value: string | string[]) {
|
|
261
263
|
if (Array.isArray(value)) {
|
|
262
264
|
value = value[0];
|
|
263
265
|
}
|
|
264
266
|
return value;
|
|
265
267
|
}
|
|
266
268
|
|
|
267
|
-
function arrayValue(value) {
|
|
269
|
+
function arrayValue(value: string | string[]) {
|
|
268
270
|
if (!Array.isArray(value)) {
|
|
269
271
|
value = [ value ];
|
|
270
272
|
}
|
|
271
273
|
return value;
|
|
272
274
|
}
|
|
273
275
|
|
|
274
|
-
function getFromHeaders(
|
|
276
|
+
function getFromHeaders(request: Request, names: string) {
|
|
275
277
|
if (!names) return '';
|
|
276
|
-
|
|
277
|
-
for (const name of
|
|
278
|
-
const value =
|
|
278
|
+
const fields = names.split(/\s*,\s*/);
|
|
279
|
+
for (const name of fields) {
|
|
280
|
+
const value = request.get<string>(name);
|
|
279
281
|
if (value) return value;
|
|
280
282
|
}
|
|
281
283
|
return '';
|