@e22m4u/ts-rest-router 0.6.7 → 0.6.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 +282 -192
- package/dist/cjs/index.cjs +4 -4
- package/dist/esm/controller-registry.js +1 -1
- package/dist/esm/decorators/request-data/request-data-decorator.js +2 -2
- package/dist/esm/decorators/request-data/request-data-decorator.spec.js +5 -5
- package/dist/esm/decorators/request-data/request-data-metadata.d.ts +1 -1
- package/dist/esm/decorators/request-data/request-data-metadata.js +1 -1
- package/package.json +9 -9
- package/src/controller-registry.ts +1 -1
- package/src/decorators/request-data/request-data-decorator.spec.ts +5 -5
- package/src/decorators/request-data/request-data-decorator.ts +2 -2
- package/src/decorators/request-data/request-data-metadata.ts +1 -1
package/README.md
CHANGED
|
@@ -1,53 +1,31 @@
|
|
|
1
1
|
# @e22m4u/ts-rest-router
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- **Декларативная маршрутизация**
|
|
13
|
-
Определение маршрутов непосредственно над методами контроллера с помощью
|
|
14
|
-
декораторов (`@getAction`, `@postAction` и т.д.).
|
|
15
|
-
- **Типобезопасная обработка данных**
|
|
16
|
-
Автоматическое извлечение, преобразование и валидация данных из `body`,
|
|
17
|
-
`query`, `params`, `headers` и `cookie` с привязкой к типизированным
|
|
18
|
-
аргументам методов.
|
|
19
|
-
- **Встроенная валидация**
|
|
20
|
-
Использование схем данных из
|
|
21
|
-
[@e22m4u/js-data-schema](https://www.npmjs.com/package/@e22m4u/js-data-schema)
|
|
22
|
-
для описания сложных правил проверки.
|
|
23
|
-
- **Хуки (`@beforeAction`, `@afterAction`)**
|
|
24
|
-
Поддержка хуков для выполнения сквозной логики (например, аутентификация
|
|
25
|
-
или логирование) на уровне контроллера и отдельных методов.
|
|
26
|
-
- **Изоляция запросов**
|
|
27
|
-
Обработка каждого запроса в отдельном DI-контейнере, что гарантирует
|
|
28
|
-
отсутствие конфликтов состояний и повышает надежность приложения.
|
|
29
|
-
- **Производительность и гибкая архитектура**
|
|
30
|
-
Основан на
|
|
31
|
-
[@e22m4u/js-trie-router](https://www.npmjs.com/package/@e22m4u/js-trie-router)
|
|
32
|
-
для маршрутизации на базе _префиксного дерева_ и
|
|
33
|
-
[@e22m4u/js-service](https://www.npmjs.com/package/@e22m4u/js-service)
|
|
34
|
-
для внедрения зависимостей.
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
REST-маршрутизатор на основе контроллеров и TypeScript декораторов.
|
|
7
|
+
|
|
8
|
+
Данный модуль позволяет создавать структурированное и масштабируемое REST API.
|
|
9
|
+
В его основе лежит декларативный подход с использованием TypeScript декораторов
|
|
10
|
+
для определения маршрутов, обработки входящих данных и управления жизненным
|
|
11
|
+
циклом запроса.
|
|
35
12
|
|
|
36
13
|
## Содержание
|
|
37
14
|
|
|
38
15
|
- [Установка](#установка)
|
|
39
|
-
- [
|
|
40
|
-
- [
|
|
41
|
-
- [URL-параметры
|
|
42
|
-
- [Query-параметры
|
|
43
|
-
- [Тело запроса
|
|
44
|
-
- [Заголовки
|
|
45
|
-
- [
|
|
46
|
-
- [
|
|
47
|
-
- [
|
|
48
|
-
- [
|
|
49
|
-
- [
|
|
50
|
-
- [
|
|
16
|
+
- [Базовый пример](#базовый-пример)
|
|
17
|
+
- [Данные запроса](#данные-запроса)
|
|
18
|
+
- [URL-параметры](#url-параметры)
|
|
19
|
+
- [Query-параметры](#query-параметры)
|
|
20
|
+
- [Тело запроса](#тело-запроса)
|
|
21
|
+
- [Заголовки](#заголовки)
|
|
22
|
+
- [Cookies](#cookies)
|
|
23
|
+
- [Контекст запроса](#контекст-запроса)
|
|
24
|
+
- [Валидация данных](#валидация-данных)
|
|
25
|
+
- [Хуки](#хуки)
|
|
26
|
+
- [Архитектура](#архитектура)
|
|
27
|
+
- [Производительность](#производительность)
|
|
28
|
+
- [Декораторы](#декораторы)
|
|
51
29
|
- [Отладка](#отладка)
|
|
52
30
|
- [Тесты](#тесты)
|
|
53
31
|
- [Лицензия](#лицензия)
|
|
@@ -70,7 +48,7 @@ npm install @e22m4u/ts-rest-router
|
|
|
70
48
|
}
|
|
71
49
|
```
|
|
72
50
|
|
|
73
|
-
##
|
|
51
|
+
## Базовый пример
|
|
74
52
|
|
|
75
53
|
Пример создания простого сервера для управления списком пользователей.
|
|
76
54
|
|
|
@@ -78,34 +56,34 @@ npm install @e22m4u/ts-rest-router
|
|
|
78
56
|
|
|
79
57
|
```ts
|
|
80
58
|
import {
|
|
81
|
-
|
|
59
|
+
DataType,
|
|
82
60
|
getAction,
|
|
83
61
|
postAction,
|
|
84
62
|
requestBody,
|
|
85
|
-
|
|
63
|
+
restController,
|
|
86
64
|
} from '@e22m4u/ts-rest-router';
|
|
87
65
|
|
|
88
|
-
//
|
|
66
|
+
// временное хранилище данных
|
|
89
67
|
const users = [{id: 1, name: 'John Doe'}];
|
|
90
68
|
|
|
91
|
-
//
|
|
92
|
-
//
|
|
69
|
+
// декоратор @restController определяет класс как контроллер
|
|
70
|
+
// и устанавливает базовый путь для всех его маршрутов
|
|
93
71
|
@restController('users')
|
|
94
72
|
export class UserController {
|
|
95
|
-
//
|
|
96
|
-
//
|
|
73
|
+
// декоратор @getAction создает маршрут для GET-запросов,
|
|
74
|
+
// полный путь: GET /users
|
|
97
75
|
@getAction()
|
|
98
76
|
getAllUsers() {
|
|
99
|
-
//
|
|
77
|
+
// результат автоматически сериализуется в JSON
|
|
100
78
|
return users;
|
|
101
79
|
}
|
|
102
80
|
|
|
103
|
-
//
|
|
104
|
-
//
|
|
81
|
+
// декоратор @postAction создает маршрут для POST-запросов,
|
|
82
|
+
// полный путь: POST /users
|
|
105
83
|
@postAction()
|
|
106
84
|
createUser(
|
|
107
|
-
//
|
|
108
|
-
//
|
|
85
|
+
// декоратор @requestBody извлекает тело запроса,
|
|
86
|
+
// проверяет его по схеме и передает в аргумент `newUser`
|
|
109
87
|
@requestBody({
|
|
110
88
|
type: DataType.OBJECT,
|
|
111
89
|
properties: {
|
|
@@ -119,7 +97,7 @@ export class UserController {
|
|
|
119
97
|
) {
|
|
120
98
|
const user = {id: users.length + 1, ...newUser};
|
|
121
99
|
users.push(user);
|
|
122
|
-
//
|
|
100
|
+
// возвращаемый объект будет отправлен клиенту как JSON
|
|
123
101
|
return user;
|
|
124
102
|
}
|
|
125
103
|
}
|
|
@@ -133,15 +111,14 @@ import {UserController} from './user.controller';
|
|
|
133
111
|
import {RestRouter} from '@e22m4u/ts-rest-router';
|
|
134
112
|
|
|
135
113
|
async function bootstrap() {
|
|
136
|
-
//
|
|
114
|
+
// создание экземпляра роутера
|
|
137
115
|
const router = new RestRouter();
|
|
138
|
-
//
|
|
116
|
+
// регистрация контроллера
|
|
139
117
|
router.addController(UserController);
|
|
140
|
-
|
|
141
|
-
// Создание HTTP-сервера с обработчиком запросов из роутера
|
|
118
|
+
// создание HTTP-сервера с обработчиком запросов из роутера
|
|
142
119
|
const server = http.createServer(router.requestListener);
|
|
143
120
|
|
|
144
|
-
//
|
|
121
|
+
// запуск сервера
|
|
145
122
|
server.listen(3000, () => {
|
|
146
123
|
console.log('Server is running on http://localhost:3000');
|
|
147
124
|
console.log('Try GET http://localhost:3000/users');
|
|
@@ -154,50 +131,74 @@ async function bootstrap() {
|
|
|
154
131
|
bootstrap();
|
|
155
132
|
```
|
|
156
133
|
|
|
157
|
-
##
|
|
134
|
+
## Данные запроса
|
|
135
|
+
|
|
136
|
+
Модуль предоставляет TypeScript декораторы для инъекции данных входящего
|
|
137
|
+
запроса через аргументы метода контроллера.
|
|
138
|
+
|
|
139
|
+
### URL-параметры
|
|
140
|
+
|
|
141
|
+
Извлечение динамических частей URL (например, `:id`).
|
|
142
|
+
|
|
143
|
+
Декораторы:
|
|
158
144
|
|
|
159
|
-
|
|
160
|
-
|
|
145
|
+
- `@requestParam(name, schema)`
|
|
146
|
+
\- извлечение одного параметра;
|
|
161
147
|
|
|
162
|
-
|
|
148
|
+
- `@requestParams(schema)`
|
|
149
|
+
\- извлечение всех URL-параметров в виде объекта;
|
|
163
150
|
|
|
164
|
-
|
|
151
|
+
Пример:
|
|
165
152
|
|
|
166
153
|
```ts
|
|
167
|
-
import {
|
|
154
|
+
import {
|
|
155
|
+
DataType,
|
|
156
|
+
getAction,
|
|
157
|
+
requestParam,
|
|
158
|
+
restController,
|
|
159
|
+
} from '@e22m4u/ts-rest-router';
|
|
168
160
|
|
|
169
161
|
@restController('articles')
|
|
170
162
|
class ArticleController {
|
|
171
|
-
//
|
|
163
|
+
// GET /articles/42
|
|
172
164
|
@getAction(':id')
|
|
173
165
|
getArticleById(
|
|
174
|
-
//
|
|
175
|
-
// типу "number"
|
|
166
|
+
// извлечение параметра 'id' с проверкой
|
|
167
|
+
// на соответствие типу "number"
|
|
176
168
|
@requestParam('id', DataType.NUMBER) id: number,
|
|
177
169
|
) {
|
|
178
|
-
//
|
|
179
|
-
// 400 Bad Request
|
|
170
|
+
// если id не является числом,
|
|
171
|
+
// то выбрасывается ошибка 400 Bad Request
|
|
180
172
|
return {articleId: id, content: '...'};
|
|
181
173
|
}
|
|
182
174
|
}
|
|
183
175
|
```
|
|
184
176
|
|
|
185
|
-
|
|
177
|
+
### Query-параметры
|
|
178
|
+
|
|
179
|
+
Извлечение параметров из строки запроса (например, `?sort=desc`).
|
|
186
180
|
|
|
187
|
-
|
|
188
|
-
- `@requestParams(schema)` - извлечение всех URL-параметров в виде объекта;
|
|
181
|
+
Декораторы:
|
|
189
182
|
|
|
190
|
-
|
|
183
|
+
- `@requestQuery(name, schema)`
|
|
184
|
+
\- извлечение одного query-параметра;
|
|
191
185
|
|
|
192
|
-
|
|
193
|
-
|
|
186
|
+
- `@requestQueries(schema)`
|
|
187
|
+
\- извлечение всех query-параметров в виде объекта;
|
|
188
|
+
|
|
189
|
+
Пример:
|
|
194
190
|
|
|
195
191
|
```ts
|
|
196
|
-
import {
|
|
192
|
+
import {
|
|
193
|
+
DataType,
|
|
194
|
+
getAction,
|
|
195
|
+
requestQuery,
|
|
196
|
+
restController,
|
|
197
|
+
} from '@e22m4u/ts-rest-router';
|
|
197
198
|
|
|
198
199
|
@restController('products')
|
|
199
200
|
class ProductController {
|
|
200
|
-
//
|
|
201
|
+
// GET /products/search?q=phone&limit=10
|
|
201
202
|
@getAction('search')
|
|
202
203
|
searchProducts(
|
|
203
204
|
@requestQuery('q', {
|
|
@@ -211,19 +212,14 @@ class ProductController {
|
|
|
211
212
|
})
|
|
212
213
|
limit: number,
|
|
213
214
|
) {
|
|
214
|
-
// searchTerm будет 'phone', limit будет 10
|
|
215
|
-
//
|
|
216
|
-
// будет использовано значение по умолчанию 20
|
|
215
|
+
// searchTerm будет 'phone', limit будет 10;
|
|
216
|
+
// при отсутствии 'q' будет ошибка;
|
|
217
|
+
// при отсутствии 'limit' будет использовано значение по умолчанию 20;
|
|
217
218
|
return {results: [], query: {searchTerm, limit}};
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
221
|
```
|
|
221
222
|
|
|
222
|
-
**Декораторы:**
|
|
223
|
-
|
|
224
|
-
- `@requestQuery(name, schema)` - извлечение одного query-параметра;
|
|
225
|
-
- `@requestQueries(schema)` - извлечение всех query-параметров в виде объекта;
|
|
226
|
-
|
|
227
223
|
**Именование Query-декораторов**
|
|
228
224
|
|
|
229
225
|
Разделение на декораторы в единственном `@requestQuery` и множественном
|
|
@@ -233,21 +229,32 @@ class ProductController {
|
|
|
233
229
|
извлечения и валидации отдельных значений, в то время как множественное число
|
|
234
230
|
служит для получения всех данных в виде единого объекта.
|
|
235
231
|
|
|
236
|
-
### Тело запроса
|
|
232
|
+
### Тело запроса
|
|
233
|
+
|
|
234
|
+
Извлечение тела запроса POST, PUT, PATCH методов.
|
|
235
|
+
|
|
236
|
+
Декораторы:
|
|
237
|
+
|
|
238
|
+
- `@requestBody(schema)`
|
|
239
|
+
\- извлечение тела запроса;
|
|
237
240
|
|
|
238
|
-
|
|
241
|
+
- `@requestField(name, schema)`
|
|
242
|
+
\- извлечение отдельного поля из тела запроса;
|
|
243
|
+
|
|
244
|
+
Пример:
|
|
239
245
|
|
|
240
246
|
```ts
|
|
241
247
|
import {
|
|
248
|
+
DataType,
|
|
242
249
|
postAction,
|
|
243
250
|
requestBody,
|
|
244
251
|
requestField,
|
|
245
|
-
|
|
252
|
+
restController,
|
|
246
253
|
} from '@e22m4u/ts-rest-router';
|
|
247
254
|
|
|
248
255
|
@restController('users')
|
|
249
256
|
class UserController {
|
|
250
|
-
//
|
|
257
|
+
// пример с @requestBody: получение и валидация всего тела запроса
|
|
251
258
|
@postAction()
|
|
252
259
|
createUser(
|
|
253
260
|
@requestBody({
|
|
@@ -271,7 +278,7 @@ class UserController {
|
|
|
271
278
|
return {id: 1, ...body};
|
|
272
279
|
}
|
|
273
280
|
|
|
274
|
-
//
|
|
281
|
+
// пример с @requestField: получение только одного поля из тела
|
|
275
282
|
@postAction('login')
|
|
276
283
|
login(
|
|
277
284
|
@requestField('username', DataType.STRING) username: string,
|
|
@@ -283,19 +290,89 @@ class UserController {
|
|
|
283
290
|
}
|
|
284
291
|
```
|
|
285
292
|
|
|
286
|
-
|
|
293
|
+
### Заголовки
|
|
294
|
+
|
|
295
|
+
Извлечение HTTP-заголовков запроса.
|
|
296
|
+
|
|
297
|
+
Декораторы:
|
|
298
|
+
|
|
299
|
+
- `@requestHeader(name, schema)`
|
|
300
|
+
\- извлечение отдельного заголовка;
|
|
301
|
+
|
|
302
|
+
- `@requestHeaders(schema)`
|
|
303
|
+
\- извлечение всех заголовков;
|
|
304
|
+
|
|
305
|
+
Пример:
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
import {
|
|
309
|
+
DataType,
|
|
310
|
+
getAction,
|
|
311
|
+
requestHeader,
|
|
312
|
+
} from '@e22m4u/ts-rest-router';
|
|
313
|
+
|
|
314
|
+
@restController('content')
|
|
315
|
+
class ContentController {
|
|
316
|
+
// GET /content
|
|
317
|
+
@getAction()
|
|
318
|
+
getContentForLanguage(
|
|
319
|
+
// извлечение заголовка 'Accept-Language'
|
|
320
|
+
// со значением по умолчанию 'en'
|
|
321
|
+
@requestHeader('Accept-Language', {
|
|
322
|
+
type: DataType.STRING,
|
|
323
|
+
default: 'en',
|
|
324
|
+
})
|
|
325
|
+
lang: string,
|
|
326
|
+
) {
|
|
327
|
+
// lang будет 'en', если заголовок отсутствует,
|
|
328
|
+
// или будет содержать значение заголовка, например 'ru-RU'
|
|
329
|
+
return {content: `Content in ${lang} language`};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Cookies
|
|
287
335
|
|
|
288
|
-
|
|
289
|
-
- `@requestField(name, schema)` - извлечение отдельного поля из тела запроса;
|
|
336
|
+
Извлечение разобранного заголовка Cookies входящего запроса.
|
|
290
337
|
|
|
291
|
-
|
|
338
|
+
Декораторы:
|
|
292
339
|
|
|
293
|
-
|
|
340
|
+
- `@requestCookie(name, schema)`
|
|
341
|
+
\- извлечение отдельного Cookie по ключу;
|
|
294
342
|
|
|
295
|
-
- `@
|
|
296
|
-
|
|
343
|
+
- `@requestCookies(schema)`
|
|
344
|
+
\- извлечение всех Cookies;
|
|
297
345
|
|
|
298
|
-
|
|
346
|
+
Пример:
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
import {
|
|
350
|
+
DataType,
|
|
351
|
+
getAction,
|
|
352
|
+
requestCookie,
|
|
353
|
+
} from '@e22m4u/ts-rest-router';
|
|
354
|
+
|
|
355
|
+
@restController('session')
|
|
356
|
+
class SessionController {
|
|
357
|
+
// GET /session/info
|
|
358
|
+
@getAction('info')
|
|
359
|
+
getSessionInfo(
|
|
360
|
+
// извлечение cookie с именем 'sessionId'
|
|
361
|
+
// и проверка, что значение является строкой
|
|
362
|
+
@requestCookie('sessionId', {
|
|
363
|
+
type: DataType.STRING,
|
|
364
|
+
required: true,
|
|
365
|
+
})
|
|
366
|
+
sessionId: string,
|
|
367
|
+
) {
|
|
368
|
+
// если cookie 'sessionId' отсутствует,
|
|
369
|
+
// будет выброшена ошибка 400 Bad Request
|
|
370
|
+
return {sessionId, info: 'User session data...'};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Контекст запроса
|
|
299
376
|
|
|
300
377
|
Для доступа к "сырым" объектам запроса/ответа Node.js или другим частям
|
|
301
378
|
контекста используются следующие декораторы:
|
|
@@ -304,11 +381,14 @@ class UserController {
|
|
|
304
381
|
- `@requestContext('req')` - инъекция нативного `IncomingMessage`;
|
|
305
382
|
- `@requestContext('res')` - инъекция нативного `ServerResponse`;
|
|
306
383
|
- `@requestContext('container')` - инъекция DI-контейнера запроса;
|
|
307
|
-
-
|
|
384
|
+
- Псевдонимы: `@httpRequest()`, `@httpResponse()`, `@requestContainer()`;
|
|
308
385
|
|
|
309
386
|
```ts
|
|
310
|
-
import {
|
|
311
|
-
|
|
387
|
+
import {
|
|
388
|
+
getAction,
|
|
389
|
+
requestContext,
|
|
390
|
+
RequestContext,
|
|
391
|
+
} from '@e22m4u/ts-rest-router';
|
|
312
392
|
|
|
313
393
|
@restController('system')
|
|
314
394
|
class SystemController {
|
|
@@ -324,14 +404,14 @@ class SystemController {
|
|
|
324
404
|
}
|
|
325
405
|
```
|
|
326
406
|
|
|
327
|
-
## Валидация
|
|
407
|
+
## Валидация данных
|
|
328
408
|
|
|
329
409
|
Модуль интегрирован с
|
|
330
410
|
[@e22m4u/js-data-schema](https://www.npmjs.com/package/@e22m4u/js-data-schema)
|
|
331
411
|
для гибкой проверки данных. Это дает возможность определять типы данных
|
|
332
412
|
и сложные правила.
|
|
333
413
|
|
|
334
|
-
|
|
414
|
+
Базовые типы:
|
|
335
415
|
|
|
336
416
|
- `DataType.ANY`
|
|
337
417
|
- `DataType.STRING`
|
|
@@ -340,23 +420,28 @@ class SystemController {
|
|
|
340
420
|
- `DataType.ARRAY`
|
|
341
421
|
- `DataType.OBJECT`
|
|
342
422
|
|
|
343
|
-
Для более сложных проверок используется объект
|
|
423
|
+
Для более сложных проверок используется объект схемы:
|
|
344
424
|
|
|
345
425
|
```ts
|
|
346
426
|
type DataSchema = {
|
|
347
|
-
type: DataType; //
|
|
348
|
-
required?: boolean; //
|
|
349
|
-
default?: unknown; //
|
|
350
|
-
items?: DataSchema; //
|
|
351
|
-
properties?: {[key: string]: DataSchema}; //
|
|
352
|
-
validate?: (value: any) => boolean | string; //
|
|
427
|
+
type: DataType; // обязательное поле
|
|
428
|
+
required?: boolean; // значение не может быть null или undefined
|
|
429
|
+
default?: unknown; // значение по умолчанию, если `required: false`
|
|
430
|
+
items?: DataSchema; // схема для элементов массива (для type: DataType.ARRAY)
|
|
431
|
+
properties?: {[key: string]: DataSchema}; // схема для свойств объекта
|
|
432
|
+
validate?: (value: any) => boolean | string; // пользовательская функция
|
|
353
433
|
};
|
|
354
434
|
```
|
|
355
435
|
|
|
356
|
-
|
|
436
|
+
Пример сложной валидации:
|
|
357
437
|
|
|
358
438
|
```ts
|
|
359
|
-
import {
|
|
439
|
+
import {
|
|
440
|
+
DataType,
|
|
441
|
+
postAction,
|
|
442
|
+
requestBody,
|
|
443
|
+
restController,
|
|
444
|
+
} from '@e22m4u/ts-rest-router';
|
|
360
445
|
|
|
361
446
|
@restController('orders')
|
|
362
447
|
class OrderController {
|
|
@@ -372,7 +457,7 @@ class OrderController {
|
|
|
372
457
|
products: {
|
|
373
458
|
type: DataType.ARRAY,
|
|
374
459
|
required: true,
|
|
375
|
-
//
|
|
460
|
+
// описание схемы для каждого элемента массива
|
|
376
461
|
items: {
|
|
377
462
|
type: DataType.OBJECT,
|
|
378
463
|
properties: {
|
|
@@ -383,7 +468,7 @@ class OrderController {
|
|
|
383
468
|
quantity: {
|
|
384
469
|
type: DataType.NUMBER,
|
|
385
470
|
required: true,
|
|
386
|
-
//
|
|
471
|
+
// валидатор: количество должно быть больше 0
|
|
387
472
|
validate: qty => qty > 0 || 'Quantity must be positive',
|
|
388
473
|
},
|
|
389
474
|
},
|
|
@@ -400,42 +485,46 @@ class OrderController {
|
|
|
400
485
|
}
|
|
401
486
|
```
|
|
402
487
|
|
|
403
|
-
## Хуки
|
|
488
|
+
## Хуки
|
|
404
489
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
490
|
+
Функции, выполняющиеся до или после основного метода контроллера именуются
|
|
491
|
+
«Хуками». Они предназначены для сквозной логики, такой как аутентификация,
|
|
492
|
+
логирование или модификация ответа. Применение хуков возможно как ко всему
|
|
493
|
+
контроллеру, так и к отдельному методу.
|
|
409
494
|
|
|
410
|
-
|
|
411
|
-
|
|
495
|
+
Декораторы:
|
|
496
|
+
|
|
497
|
+
- `@beforeAction(preHandler)` - выполняется перед методом контроллера;
|
|
498
|
+
- `@afterAction(postHandler)` - выполняется после метода контроллера;
|
|
499
|
+
|
|
500
|
+
Пример:
|
|
412
501
|
|
|
413
502
|
```ts
|
|
414
|
-
import {RequestContext} from '@e22m4u/js-trie-router';
|
|
415
503
|
import createError from 'http-errors';
|
|
504
|
+
import {RequestContext} from '@e22m4u/ts-rest-router';
|
|
416
505
|
|
|
417
|
-
//
|
|
506
|
+
// хук для проверки аутентификации
|
|
418
507
|
async function authHook(ctx: RequestContext) {
|
|
419
508
|
const token = ctx.headers.authorization;
|
|
420
509
|
if (!token /* || !isValidToken(token) */) {
|
|
421
|
-
//
|
|
422
|
-
// соответствующий HTTP
|
|
510
|
+
// выброс ошибки прерывает выполнение и отправляет
|
|
511
|
+
// клиенту соответствующий HTTP-статус
|
|
423
512
|
throw createError(401, 'Unauthorized');
|
|
424
513
|
}
|
|
425
514
|
}
|
|
426
515
|
|
|
427
|
-
//
|
|
516
|
+
// хук для логирования и модификации ответа
|
|
428
517
|
async function loggerHook(ctx: RequestContext, data: any) {
|
|
429
518
|
console.log(`Response for ${ctx.pathname}:`, data);
|
|
430
|
-
//
|
|
519
|
+
// хуки @afterAction могут модифицировать ответ
|
|
431
520
|
return {...data, timestamp: new Date()};
|
|
432
521
|
}
|
|
433
522
|
|
|
434
|
-
@beforeAction(authHook) //
|
|
523
|
+
@beforeAction(authHook) // применение ко всем методам контроллера
|
|
435
524
|
@restController('profile')
|
|
436
525
|
class ProfileController {
|
|
437
526
|
@getAction('me')
|
|
438
|
-
@afterAction(loggerHook) //
|
|
527
|
+
@afterAction(loggerHook) // применение только к этому методу
|
|
439
528
|
getMyProfile() {
|
|
440
529
|
return {id: 1, name: 'Current User'};
|
|
441
530
|
}
|
|
@@ -447,23 +536,23 @@ class ProfileController {
|
|
|
447
536
|
}
|
|
448
537
|
```
|
|
449
538
|
|
|
450
|
-
##
|
|
539
|
+
## Архитектура
|
|
451
540
|
|
|
452
541
|
Понимание архитектурных принципов `ts-rest-router` является ключом
|
|
453
542
|
к созданию надежных и масштабируемых приложений. Модуль построен на
|
|
454
543
|
базе библиотеки `@e22m4u/js-service`, реализующей паттерн
|
|
455
544
|
Service Locator / Dependency Injection.
|
|
456
545
|
|
|
457
|
-
####
|
|
546
|
+
#### Изоляция запросов
|
|
458
547
|
|
|
459
|
-
Для
|
|
460
|
-
экземпляр
|
|
548
|
+
Для каждого входящего HTTP-запроса создается новый, изолированный
|
|
549
|
+
экземпляр контроллера. Этот фундаментальный принцип гарантирует, что
|
|
461
550
|
состояние одного запроса (например, данные аутентифицированного
|
|
462
551
|
пользователя) никогда не будет разделено с другим, одновременно
|
|
463
552
|
обрабатываемым запросом. Это устраняет целый класс потенциальных
|
|
464
553
|
ошибок, связанных с состоянием гонки.
|
|
465
554
|
|
|
466
|
-
####
|
|
555
|
+
#### Внедрение зависимостей
|
|
467
556
|
|
|
468
557
|
Каждый экземпляр контроллера создается с помощью своего собственного
|
|
469
558
|
DI-контейнера, который существует только в рамках одного запроса. Чтобы
|
|
@@ -474,31 +563,31 @@ DI-контейнера, который существует только в р
|
|
|
474
563
|
|
|
475
564
|
**Практический пример с сервисом аутентификации:**
|
|
476
565
|
|
|
477
|
-
|
|
566
|
+
1\. Создание хука для подготовки сервиса.
|
|
478
567
|
|
|
479
|
-
|
|
480
|
-
В
|
|
568
|
+
С помощью хука можно подготовить сервис, специфичный для каждого запроса.
|
|
569
|
+
В данном примере хук `@beforeAction` создает экземпляр `AuthService`,
|
|
481
570
|
выполняет аутентификацию и регистрирует его в контейнере запроса.
|
|
482
571
|
|
|
483
572
|
```ts
|
|
484
573
|
// src/auth.hook.ts
|
|
485
574
|
import {AuthService} from './auth.service';
|
|
486
|
-
import {RequestContext} from '@e22m4u/
|
|
575
|
+
import {RequestContext} from '@e22m4u/ts-rest-router';
|
|
487
576
|
|
|
488
577
|
export async function authHook(context: RequestContext) {
|
|
489
578
|
const requestContainer = context.container;
|
|
490
|
-
//
|
|
579
|
+
// создание сервиса с передачей ему контейнера запроса
|
|
491
580
|
const authService = new AuthService(requestContainer);
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
// сможет получить
|
|
581
|
+
// регистрация созданного экземпляра в контейнере
|
|
582
|
+
// (теперь любой другой сервис в рамках текущего
|
|
583
|
+
// запроса сможет получить данный экземпляр)
|
|
495
584
|
requestContainer.set(AuthService, authService);
|
|
496
|
-
//
|
|
585
|
+
// выполнение логики аутентификации
|
|
497
586
|
await authService.authenticate(context.headers.authorization);
|
|
498
587
|
}
|
|
499
588
|
```
|
|
500
589
|
|
|
501
|
-
|
|
590
|
+
2\. Создание `AuthService`.
|
|
502
591
|
|
|
503
592
|
`AuthService` наследуется от `Service`, что позволяет ему запрашивать другие
|
|
504
593
|
зависимости (например, `this.getService(DatabaseService)`).
|
|
@@ -511,7 +600,8 @@ export class AuthService extends Service {
|
|
|
511
600
|
public currentUser?: {id: number; name: string};
|
|
512
601
|
|
|
513
602
|
async authenticate(token?: string) {
|
|
514
|
-
//
|
|
603
|
+
// логика проверки токена и поиска
|
|
604
|
+
// пользователя в базе данных...
|
|
515
605
|
if (token === 'valid-token') {
|
|
516
606
|
this.currentUser = {id: 1, name: 'John Doe'};
|
|
517
607
|
}
|
|
@@ -519,7 +609,7 @@ export class AuthService extends Service {
|
|
|
519
609
|
}
|
|
520
610
|
```
|
|
521
611
|
|
|
522
|
-
|
|
612
|
+
3\. Использование сервиса в контроллере.
|
|
523
613
|
|
|
524
614
|
Контроллер, унаследованный от `Service`, теперь может получить
|
|
525
615
|
доступ к предварительно настроенному экземпляру `AuthService`
|
|
@@ -531,18 +621,20 @@ import {authHook} from './auth.hook';
|
|
|
531
621
|
import createError from 'http-errors';
|
|
532
622
|
import {Service} from '@e22m4u/js-service';
|
|
533
623
|
import {AuthService} from './auth.service';
|
|
534
|
-
import {getAction,
|
|
624
|
+
import {getAction, beforeAction, restController} from '@e22m4u/ts-rest-router';
|
|
535
625
|
|
|
536
626
|
@beforeAction(authHook)
|
|
537
627
|
@restController('profile')
|
|
538
628
|
export class ProfileController extends Service {
|
|
539
629
|
@getAction('me')
|
|
540
630
|
getProfile() {
|
|
541
|
-
//
|
|
542
|
-
// который был создан и зарегистрирован в
|
|
631
|
+
// получение request-scoped экземпляра AuthService,
|
|
632
|
+
// который был создан и зарегистрирован в хуке
|
|
543
633
|
const authService = this.getService(AuthService);
|
|
544
634
|
|
|
545
|
-
if (!authService.currentUser)
|
|
635
|
+
if (!authService.currentUser) {
|
|
636
|
+
throw createError(401, 'Unauthorized');
|
|
637
|
+
}
|
|
546
638
|
|
|
547
639
|
return authService.currentUser;
|
|
548
640
|
}
|
|
@@ -553,12 +645,12 @@ export class ProfileController extends Service {
|
|
|
553
645
|
и контроллером, позволяя безопасно передавать состояние, изолированное
|
|
554
646
|
в рамках одного HTTP-запроса.
|
|
555
647
|
|
|
556
|
-
##
|
|
648
|
+
## Производительность
|
|
557
649
|
|
|
558
650
|
В основе данного модуля лежит
|
|
559
651
|
[@e22m4u/js-trie-router](https://www.npmjs.com/package/@e22m4u/js-trie-router),
|
|
560
652
|
который использует структуру данных
|
|
561
|
-
|
|
653
|
+
*префиксного дерева ([Trie](https://en.wikipedia.org/wiki/Trie))*
|
|
562
654
|
для хранения маршрутов и их сопоставления. Такое архитектурное решение
|
|
563
655
|
обеспечивает высокую производительность, особенно в приложениях
|
|
564
656
|
с большим количеством маршрутов.
|
|
@@ -574,41 +666,39 @@ export class ProfileController extends Service {
|
|
|
574
666
|
маршрутизатор не перебирает все существующие маршруты. Вместо этого
|
|
575
667
|
он последовательно спускается по дереву:
|
|
576
668
|
|
|
577
|
-
1.
|
|
578
|
-
2.
|
|
579
|
-
3.
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
4.
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
## Полный список декораторов
|
|
669
|
+
1. Находит корневой узел для метода `GET`.
|
|
670
|
+
2. От него переходит к дочернему узлу `users`.
|
|
671
|
+
3. Далее, не найдя статического узла `123`, он ищет динамический
|
|
672
|
+
узел (`:id`) и сопоставляет его, сохраняя `123` как значение
|
|
673
|
+
параметра `id`.
|
|
674
|
+
4. Наконец, он переходит к узлу `posts` и находит совпадение.
|
|
675
|
+
|
|
676
|
+
**Эффективный поиск маршрута O(k)**
|
|
677
|
+
|
|
678
|
+
Самое главное преимущество - скорость поиска. Вместо того чтобы
|
|
679
|
+
перебирать массив из `N` маршрутов и проверять каждый с помощью
|
|
680
|
+
регулярного выражения (сложность `O(N)`), префиксное дерево
|
|
681
|
+
находит совпадение за время, пропорциональное количеству сегментов
|
|
682
|
+
`K` в URL-пути (сложность `O(K)`). Это означает, что производительность
|
|
683
|
+
поиска не падает с ростом общего числа маршрутов в приложении.
|
|
684
|
+
|
|
685
|
+
**Быстрая обработка 404 (ранний выход)**
|
|
686
|
+
|
|
687
|
+
Если приходит запрос на несуществующий путь, например
|
|
688
|
+
`/users/123/comments`, поиск по дереву прекратится сразу после
|
|
689
|
+
того, как маршрутизатор не найдет дочерний узел `comments`
|
|
690
|
+
у узла `:id`. Ему не нужно проверять остальные сотни маршрутов,
|
|
691
|
+
чтобы убедиться, что совпадений нет. Это делает обработку
|
|
692
|
+
неопределенных маршрутов (404) почти мгновенной.
|
|
693
|
+
|
|
694
|
+
**Оптимизация для статических и динамических маршрутов**
|
|
695
|
+
|
|
696
|
+
При поиске маршрутизатор всегда отдает приоритет статическим
|
|
697
|
+
сегментам перед динамическими. Маршрут `/users/me` всегда будет
|
|
698
|
+
найден раньше и быстрее, чем `/users/:id` при запросе `/users/me`,
|
|
699
|
+
поскольку не требуется проверка на соответствие шаблону.
|
|
700
|
+
|
|
701
|
+
## Декораторы
|
|
612
702
|
|
|
613
703
|
#### Контроллер и методы:
|
|
614
704
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -205,7 +205,7 @@ var RequestDataSource;
|
|
|
205
205
|
RequestDataSource2["PARAMS"] = "params";
|
|
206
206
|
RequestDataSource2["QUERY"] = "query";
|
|
207
207
|
RequestDataSource2["HEADERS"] = "headers";
|
|
208
|
-
RequestDataSource2["
|
|
208
|
+
RequestDataSource2["COOKIES"] = "cookies";
|
|
209
209
|
RequestDataSource2["BODY"] = "body";
|
|
210
210
|
})(RequestDataSource || (RequestDataSource = {}));
|
|
211
211
|
var REQUEST_DATA_METADATA_KEY = new import_ts_reflector5.MetadataKey("requestDataMetadataKey");
|
|
@@ -305,8 +305,8 @@ var requestQueries = createRequestDataDecoratorWithSource(RequestDataSource.QUER
|
|
|
305
305
|
var requestQuery = createRequestDataPropertyDecoratorWithSource(RequestDataSource.QUERY);
|
|
306
306
|
var requestHeaders = createRequestDataDecoratorWithSource(RequestDataSource.HEADERS);
|
|
307
307
|
var requestHeader = createRequestDataPropertyDecoratorWithSource(RequestDataSource.HEADERS);
|
|
308
|
-
var requestCookies = createRequestDataDecoratorWithSource(RequestDataSource.
|
|
309
|
-
var requestCookie = createRequestDataPropertyDecoratorWithSource(RequestDataSource.
|
|
308
|
+
var requestCookies = createRequestDataDecoratorWithSource(RequestDataSource.COOKIES);
|
|
309
|
+
var requestCookie = createRequestDataPropertyDecoratorWithSource(RequestDataSource.COOKIES);
|
|
310
310
|
var requestBody = createRequestDataDecoratorWithSource(RequestDataSource.BODY);
|
|
311
311
|
var requestField = createRequestDataPropertyDecoratorWithSource(RequestDataSource.BODY);
|
|
312
312
|
|
|
@@ -896,7 +896,7 @@ var _ControllerRegistry = class _ControllerRegistry extends DebuggableService {
|
|
|
896
896
|
case RequestDataSource.HEADERS:
|
|
897
897
|
data = requestContext2.headers;
|
|
898
898
|
break;
|
|
899
|
-
case RequestDataSource.
|
|
899
|
+
case RequestDataSource.COOKIES:
|
|
900
900
|
data = requestContext2.cookies;
|
|
901
901
|
break;
|
|
902
902
|
case RequestDataSource.BODY:
|
|
@@ -372,7 +372,7 @@ export class ControllerRegistry extends DebuggableService {
|
|
|
372
372
|
case RequestDataSource.HEADERS:
|
|
373
373
|
data = requestContext.headers;
|
|
374
374
|
break;
|
|
375
|
-
case RequestDataSource.
|
|
375
|
+
case RequestDataSource.COOKIES:
|
|
376
376
|
data = requestContext.cookies;
|
|
377
377
|
break;
|
|
378
378
|
case RequestDataSource.BODY:
|
|
@@ -82,7 +82,7 @@ export const requestQueries = createRequestDataDecoratorWithSource(RequestDataSo
|
|
|
82
82
|
export const requestQuery = createRequestDataPropertyDecoratorWithSource(RequestDataSource.QUERY);
|
|
83
83
|
export const requestHeaders = createRequestDataDecoratorWithSource(RequestDataSource.HEADERS);
|
|
84
84
|
export const requestHeader = createRequestDataPropertyDecoratorWithSource(RequestDataSource.HEADERS);
|
|
85
|
-
export const requestCookies = createRequestDataDecoratorWithSource(RequestDataSource.
|
|
86
|
-
export const requestCookie = createRequestDataPropertyDecoratorWithSource(RequestDataSource.
|
|
85
|
+
export const requestCookies = createRequestDataDecoratorWithSource(RequestDataSource.COOKIES);
|
|
86
|
+
export const requestCookie = createRequestDataPropertyDecoratorWithSource(RequestDataSource.COOKIES);
|
|
87
87
|
export const requestBody = createRequestDataDecoratorWithSource(RequestDataSource.BODY);
|
|
88
88
|
export const requestField = createRequestDataPropertyDecoratorWithSource(RequestDataSource.BODY);
|
|
@@ -126,7 +126,7 @@ describe('requestData', function () {
|
|
|
126
126
|
], Target.prototype, "myMethod", null);
|
|
127
127
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
128
128
|
expect(res.get(0)).to.be.eql({
|
|
129
|
-
source: RequestDataSource.
|
|
129
|
+
source: RequestDataSource.COOKIES,
|
|
130
130
|
schema: { type: DataType.ANY },
|
|
131
131
|
});
|
|
132
132
|
});
|
|
@@ -554,7 +554,7 @@ describe('requestData', function () {
|
|
|
554
554
|
], Target.prototype, "myMethod", null);
|
|
555
555
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
556
556
|
expect(res.get(0)).to.be.eql({
|
|
557
|
-
source: RequestDataSource.
|
|
557
|
+
source: RequestDataSource.COOKIES,
|
|
558
558
|
schema: {
|
|
559
559
|
type: DataType.OBJECT,
|
|
560
560
|
properties: {
|
|
@@ -580,7 +580,7 @@ describe('requestData', function () {
|
|
|
580
580
|
], Target.prototype, "myMethod", null);
|
|
581
581
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
582
582
|
expect(res.get(0)).to.be.eql({
|
|
583
|
-
source: RequestDataSource.
|
|
583
|
+
source: RequestDataSource.COOKIES,
|
|
584
584
|
schema: {
|
|
585
585
|
type: DataType.OBJECT,
|
|
586
586
|
properties: {
|
|
@@ -609,7 +609,7 @@ describe('requestData', function () {
|
|
|
609
609
|
], Target.prototype, "myMethod", null);
|
|
610
610
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
611
611
|
expect(res.get(0)).to.be.eql({
|
|
612
|
-
source: RequestDataSource.
|
|
612
|
+
source: RequestDataSource.COOKIES,
|
|
613
613
|
schema: {
|
|
614
614
|
type: DataType.OBJECT,
|
|
615
615
|
properties: {
|
|
@@ -637,7 +637,7 @@ describe('requestData', function () {
|
|
|
637
637
|
], Target.prototype, "myMethod", null);
|
|
638
638
|
const mdMap = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
639
639
|
const md = mdMap.get(0);
|
|
640
|
-
expect(md.source).to.be.eq(RequestDataSource.
|
|
640
|
+
expect(md.source).to.be.eq(RequestDataSource.COOKIES);
|
|
641
641
|
expect(md.schema).to.be.a('function');
|
|
642
642
|
expect(md.property).to.be.eq(propertyKey);
|
|
643
643
|
const res1 = md.schema;
|
|
@@ -7,7 +7,7 @@ export var RequestDataSource;
|
|
|
7
7
|
RequestDataSource["PARAMS"] = "params";
|
|
8
8
|
RequestDataSource["QUERY"] = "query";
|
|
9
9
|
RequestDataSource["HEADERS"] = "headers";
|
|
10
|
-
RequestDataSource["
|
|
10
|
+
RequestDataSource["COOKIES"] = "cookies";
|
|
11
11
|
RequestDataSource["BODY"] = "body";
|
|
12
12
|
})(RequestDataSource || (RequestDataSource = {}));
|
|
13
13
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e22m4u/ts-rest-router",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "Декларативный REST-маршрутизатор на основе контроллеров для TypeScript",
|
|
5
5
|
"author": "Mikhail Evstropov <e22m4u@yandex.ru>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@e22m4u/js-data-schema": "~0.4.6",
|
|
45
45
|
"@e22m4u/js-debug": "~0.3.2",
|
|
46
46
|
"@e22m4u/js-format": "~0.2.0",
|
|
47
|
-
"@e22m4u/js-service": "~0.4.
|
|
47
|
+
"@e22m4u/js-service": "~0.4.5",
|
|
48
48
|
"@e22m4u/js-trie-router": "~0.3.4",
|
|
49
49
|
"@e22m4u/ts-reflector": "~0.1.8",
|
|
50
50
|
"http-errors": "~2.0.0"
|
|
@@ -52,25 +52,25 @@
|
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@commitlint/cli": "~20.1.0",
|
|
54
54
|
"@commitlint/config-conventional": "~20.0.0",
|
|
55
|
-
"@eslint/js": "~9.
|
|
55
|
+
"@eslint/js": "~9.39.1",
|
|
56
56
|
"@types/chai": "~5.2.3",
|
|
57
57
|
"@types/debug": "~4.1.12",
|
|
58
58
|
"@types/http-errors": "~2.0.5",
|
|
59
59
|
"@types/mocha": "~10.0.10",
|
|
60
|
-
"@types/node": "~24.
|
|
60
|
+
"@types/node": "~24.10.0",
|
|
61
61
|
"c8": "~10.1.3",
|
|
62
62
|
"chai": "~6.2.0",
|
|
63
|
-
"esbuild": "~0.25.
|
|
64
|
-
"eslint": "~9.
|
|
63
|
+
"esbuild": "~0.25.12",
|
|
64
|
+
"eslint": "~9.39.1",
|
|
65
65
|
"eslint-config-prettier": "~10.1.8",
|
|
66
66
|
"eslint-plugin-chai-expect": "~3.1.0",
|
|
67
67
|
"eslint-plugin-mocha": "~11.2.0",
|
|
68
68
|
"husky": "~9.1.7",
|
|
69
|
-
"mocha": "~11.7.
|
|
69
|
+
"mocha": "~11.7.5",
|
|
70
70
|
"prettier": "~3.6.2",
|
|
71
|
-
"rimraf": "~6.0
|
|
71
|
+
"rimraf": "~6.1.0",
|
|
72
72
|
"tsx": "~4.20.6",
|
|
73
73
|
"typescript": "~5.9.3",
|
|
74
|
-
"typescript-eslint": "~8.46.
|
|
74
|
+
"typescript-eslint": "~8.46.3"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -450,7 +450,7 @@ export class ControllerRegistry extends DebuggableService {
|
|
|
450
450
|
case RequestDataSource.HEADERS:
|
|
451
451
|
data = requestContext.headers;
|
|
452
452
|
break;
|
|
453
|
-
case RequestDataSource.
|
|
453
|
+
case RequestDataSource.COOKIES:
|
|
454
454
|
data = requestContext.cookies;
|
|
455
455
|
break;
|
|
456
456
|
case RequestDataSource.BODY:
|
|
@@ -108,7 +108,7 @@ describe('requestData', function () {
|
|
|
108
108
|
}
|
|
109
109
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
110
110
|
expect(res.get(0)).to.be.eql({
|
|
111
|
-
source: RequestDataSource.
|
|
111
|
+
source: RequestDataSource.COOKIES,
|
|
112
112
|
schema: {type: DataType.ANY},
|
|
113
113
|
});
|
|
114
114
|
});
|
|
@@ -502,7 +502,7 @@ describe('requestData', function () {
|
|
|
502
502
|
}
|
|
503
503
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
504
504
|
expect(res.get(0)).to.be.eql({
|
|
505
|
-
source: RequestDataSource.
|
|
505
|
+
source: RequestDataSource.COOKIES,
|
|
506
506
|
schema: {
|
|
507
507
|
type: DataType.OBJECT,
|
|
508
508
|
properties: {
|
|
@@ -526,7 +526,7 @@ describe('requestData', function () {
|
|
|
526
526
|
}
|
|
527
527
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
528
528
|
expect(res.get(0)).to.be.eql({
|
|
529
|
-
source: RequestDataSource.
|
|
529
|
+
source: RequestDataSource.COOKIES,
|
|
530
530
|
schema: {
|
|
531
531
|
type: DataType.OBJECT,
|
|
532
532
|
properties: {
|
|
@@ -553,7 +553,7 @@ describe('requestData', function () {
|
|
|
553
553
|
}
|
|
554
554
|
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
555
555
|
expect(res.get(0)).to.be.eql({
|
|
556
|
-
source: RequestDataSource.
|
|
556
|
+
source: RequestDataSource.COOKIES,
|
|
557
557
|
schema: {
|
|
558
558
|
type: DataType.OBJECT,
|
|
559
559
|
properties: {
|
|
@@ -579,7 +579,7 @@ describe('requestData', function () {
|
|
|
579
579
|
}
|
|
580
580
|
const mdMap = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
581
581
|
const md = mdMap.get(0) as RequestDataMetadata;
|
|
582
|
-
expect(md.source).to.be.eq(RequestDataSource.
|
|
582
|
+
expect(md.source).to.be.eq(RequestDataSource.COOKIES);
|
|
583
583
|
expect(md.schema).to.be.a('function');
|
|
584
584
|
expect(md.property).to.be.eq(propertyKey);
|
|
585
585
|
const res1 = md.schema as DataSchemaFactory;
|
|
@@ -118,10 +118,10 @@ export const requestHeader = createRequestDataPropertyDecoratorWithSource(
|
|
|
118
118
|
RequestDataSource.HEADERS,
|
|
119
119
|
);
|
|
120
120
|
export const requestCookies = createRequestDataDecoratorWithSource(
|
|
121
|
-
RequestDataSource.
|
|
121
|
+
RequestDataSource.COOKIES,
|
|
122
122
|
);
|
|
123
123
|
export const requestCookie = createRequestDataPropertyDecoratorWithSource(
|
|
124
|
-
RequestDataSource.
|
|
124
|
+
RequestDataSource.COOKIES,
|
|
125
125
|
);
|
|
126
126
|
export const requestBody = createRequestDataDecoratorWithSource(
|
|
127
127
|
RequestDataSource.BODY,
|