@loopback/rest 5.2.0 → 6.2.0
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/CHANGELOG.md +73 -0
- package/README.md +4 -0
- package/dist/body-parsers/body-parser.js +3 -0
- package/dist/body-parsers/body-parser.js.map +1 -1
- package/dist/coercion/coerce-parameter.js +3 -2
- package/dist/coercion/coerce-parameter.js.map +1 -1
- package/dist/coercion/utils.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/keys.d.ts +18 -3
- package/dist/keys.js +18 -1
- package/dist/keys.js.map +1 -1
- package/dist/parser.d.ts +2 -2
- package/dist/parser.js +3 -2
- package/dist/parser.js.map +1 -1
- package/dist/providers/find-route.provider.d.ts +7 -1
- package/dist/providers/find-route.provider.js +29 -1
- package/dist/providers/find-route.provider.js.map +1 -1
- package/dist/providers/invoke-method.provider.d.ts +7 -1
- package/dist/providers/invoke-method.provider.js +43 -1
- package/dist/providers/invoke-method.provider.js.map +1 -1
- package/dist/providers/parse-params.provider.d.ts +8 -2
- package/dist/providers/parse-params.provider.js +32 -2
- package/dist/providers/parse-params.provider.js.map +1 -1
- package/dist/providers/send.provider.d.ts +10 -2
- package/dist/providers/send.provider.js +46 -1
- package/dist/providers/send.provider.js.map +1 -1
- package/dist/request-context.js.map +1 -1
- package/dist/rest-http-error.d.ts +3 -1
- package/dist/rest-http-error.js +3 -2
- package/dist/rest-http-error.js.map +1 -1
- package/dist/rest.application.js +1 -1
- package/dist/rest.application.js.map +1 -1
- package/dist/rest.component.d.ts +1 -2
- package/dist/rest.component.js +23 -7
- package/dist/rest.component.js.map +1 -1
- package/dist/rest.server.d.ts +8 -4
- package/dist/rest.server.js +25 -12
- package/dist/rest.server.js.map +1 -1
- package/dist/router/base-route.js +3 -3
- package/dist/router/base-route.js.map +1 -1
- package/dist/router/controller-route.js +1 -1
- package/dist/router/controller-route.js.map +1 -1
- package/dist/router/handler-route.js +1 -1
- package/dist/router/handler-route.js.map +1 -1
- package/dist/router/redirect-route.js +1 -1
- package/dist/router/redirect-route.js.map +1 -1
- package/dist/sequence.d.ts +84 -2
- package/dist/sequence.js +135 -2
- package/dist/sequence.js.map +1 -1
- package/dist/types.d.ts +9 -4
- package/dist/validation/ajv-factory.provider.d.ts +3 -2
- package/dist/validation/ajv-factory.provider.js +7 -2
- package/dist/validation/ajv-factory.provider.js.map +1 -1
- package/dist/validation/request-body.validator.d.ts +2 -2
- package/dist/validation/request-body.validator.js +11 -13
- package/dist/validation/request-body.validator.js.map +1 -1
- package/package.json +25 -25
- package/src/body-parsers/body-parser.ts +3 -0
- package/src/coercion/coerce-parameter.ts +4 -3
- package/src/keys.ts +34 -3
- package/src/parser.ts +4 -3
- package/src/providers/find-route.provider.ts +36 -3
- package/src/providers/invoke-method.provider.ts +47 -3
- package/src/providers/parse-params.provider.ts +36 -9
- package/src/providers/send.provider.ts +45 -2
- package/src/request-context.ts +2 -1
- package/src/rest-http-error.ts +6 -2
- package/src/rest.application.ts +1 -1
- package/src/rest.component.ts +40 -10
- package/src/rest.server.ts +50 -16
- package/src/router/base-route.ts +3 -3
- package/src/router/controller-route.ts +1 -1
- package/src/router/handler-route.ts +3 -1
- package/src/router/redirect-route.ts +1 -1
- package/src/sequence.ts +174 -4
- package/src/types.ts +13 -4
- package/src/validation/ajv-factory.provider.ts +10 -8
- package/src/validation/request-body.validator.ts +28 -24
|
@@ -11,12 +11,13 @@ import {
|
|
|
11
11
|
} from '@loopback/openapi-v3';
|
|
12
12
|
import debugModule from 'debug';
|
|
13
13
|
import {
|
|
14
|
-
RequestBodyValidationOptions,
|
|
15
14
|
RestHttpErrors,
|
|
16
15
|
validateValueAgainstSchema,
|
|
16
|
+
ValidationOptions,
|
|
17
17
|
ValueValidationOptions,
|
|
18
18
|
} from '../';
|
|
19
19
|
import {parseJson} from '../parse-json';
|
|
20
|
+
import {DEFAULT_AJV_VALIDATION_OPTIONS} from '../validation/ajv-factory.provider';
|
|
20
21
|
import {
|
|
21
22
|
DateCoercionOptions,
|
|
22
23
|
getOAIPrimitiveType,
|
|
@@ -167,7 +168,7 @@ function coerceBoolean(data: string | object, spec: ParameterObject) {
|
|
|
167
168
|
async function coerceObject(
|
|
168
169
|
input: string | object,
|
|
169
170
|
spec: ParameterObject,
|
|
170
|
-
options
|
|
171
|
+
options: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
|
|
171
172
|
) {
|
|
172
173
|
const data = parseJsonIfNeeded(input, spec);
|
|
173
174
|
|
|
@@ -186,7 +187,7 @@ async function coerceObject(
|
|
|
186
187
|
data,
|
|
187
188
|
schema,
|
|
188
189
|
{},
|
|
189
|
-
{...options, coerceTypes: true, source: 'parameter'},
|
|
190
|
+
{...options, coerceTypes: true, source: 'parameter', name: spec.name},
|
|
190
191
|
);
|
|
191
192
|
}
|
|
192
193
|
|
package/src/keys.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
6
|
import {BindingKey, Context, CoreBindings} from '@loopback/core';
|
|
7
|
-
import {InvokeMiddleware} from '@loopback/express';
|
|
7
|
+
import {DEFAULT_MIDDLEWARE_CHAIN, InvokeMiddleware} from '@loopback/express';
|
|
8
8
|
import {HttpProtocol} from '@loopback/http-server';
|
|
9
9
|
import {OpenApiSpec, OperationObject} from '@loopback/openapi-v3';
|
|
10
10
|
import https from 'https';
|
|
@@ -12,13 +12,15 @@ import {ErrorWriterOptions} from 'strong-error-handler';
|
|
|
12
12
|
import {BodyParser, RequestBodyParser} from './body-parsers';
|
|
13
13
|
import {HttpHandler} from './http-handler';
|
|
14
14
|
import {RestServer, RestServerConfig} from './rest.server';
|
|
15
|
-
import {RestRouter, RestRouterOptions} from './router';
|
|
15
|
+
import {ResolvedRoute, RestRouter, RestRouterOptions} from './router';
|
|
16
16
|
import {SequenceHandler} from './sequence';
|
|
17
17
|
import {
|
|
18
18
|
AjvFactory,
|
|
19
19
|
FindRoute,
|
|
20
20
|
InvokeMethod,
|
|
21
21
|
LogError,
|
|
22
|
+
OperationArgs,
|
|
23
|
+
OperationRetval,
|
|
22
24
|
ParseParams,
|
|
23
25
|
Reject,
|
|
24
26
|
Request,
|
|
@@ -179,12 +181,20 @@ export namespace RestBindings {
|
|
|
179
181
|
*/
|
|
180
182
|
export const SEQUENCE = BindingKey.create<SequenceHandler>('rest.sequence');
|
|
181
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Binding key for setting and injecting a `invokeMiddleware` function for
|
|
186
|
+
* middleware based sequence
|
|
187
|
+
*/
|
|
188
|
+
export const INVOKE_MIDDLEWARE_SERVICE = BindingKey.create<InvokeMiddleware>(
|
|
189
|
+
'rest.invokeMiddleware',
|
|
190
|
+
);
|
|
191
|
+
|
|
182
192
|
/**
|
|
183
193
|
* Bindings for potential actions that could be used in a sequence
|
|
184
194
|
*/
|
|
185
195
|
export namespace SequenceActions {
|
|
186
196
|
/**
|
|
187
|
-
* Binding key for setting and injecting
|
|
197
|
+
* Binding key for setting and injecting `invokeMiddleware` function
|
|
188
198
|
*/
|
|
189
199
|
export const INVOKE_MIDDLEWARE = BindingKey.create<InvokeMiddleware>(
|
|
190
200
|
'rest.sequence.actions.invokeMiddleware',
|
|
@@ -225,6 +235,20 @@ export namespace RestBindings {
|
|
|
225
235
|
);
|
|
226
236
|
}
|
|
227
237
|
|
|
238
|
+
export namespace Operation {
|
|
239
|
+
export const ROUTE = BindingKey.create<ResolvedRoute>(
|
|
240
|
+
'rest.operation.route',
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
export const PARAMS = BindingKey.create<OperationArgs>(
|
|
244
|
+
'rest.operation.params',
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
export const RETURN_VALUE = BindingKey.create<OperationRetval>(
|
|
248
|
+
'rest.operation.returnValue',
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
228
252
|
/**
|
|
229
253
|
* Request-specific bindings
|
|
230
254
|
*/
|
|
@@ -285,4 +309,11 @@ export namespace RestTags {
|
|
|
285
309
|
|
|
286
310
|
export const AJV_KEYWORD = 'ajvKeyword';
|
|
287
311
|
export const AJV_FORMAT = 'ajvFormat';
|
|
312
|
+
|
|
313
|
+
export const REST_MIDDLEWARE_CHAIN = DEFAULT_MIDDLEWARE_CHAIN;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Legacy middleware chain for action-based REST sequence
|
|
317
|
+
*/
|
|
318
|
+
export const ACTION_MIDDLEWARE_CHAIN = 'middlewareChain.rest.actions';
|
|
288
319
|
}
|
package/src/parser.ts
CHANGED
|
@@ -19,8 +19,9 @@ import {
|
|
|
19
19
|
OperationArgs,
|
|
20
20
|
PathParameterValues,
|
|
21
21
|
Request,
|
|
22
|
-
|
|
22
|
+
ValidationOptions,
|
|
23
23
|
} from './types';
|
|
24
|
+
import {DEFAULT_AJV_VALIDATION_OPTIONS} from './validation/ajv-factory.provider';
|
|
24
25
|
import {validateRequestBody} from './validation/request-body.validator';
|
|
25
26
|
const debug = debugFactory('loopback:rest:parser');
|
|
26
27
|
|
|
@@ -35,7 +36,7 @@ export async function parseOperationArgs(
|
|
|
35
36
|
request: Request,
|
|
36
37
|
route: ResolvedRoute,
|
|
37
38
|
requestBodyParser: RequestBodyParser = new RequestBodyParser(),
|
|
38
|
-
options:
|
|
39
|
+
options: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
|
|
39
40
|
): Promise<OperationArgs> {
|
|
40
41
|
debug('Parsing operation arguments for route %s', route.describe());
|
|
41
42
|
const operationSpec = route.spec;
|
|
@@ -60,7 +61,7 @@ async function buildOperationArguments(
|
|
|
60
61
|
pathParams: PathParameterValues,
|
|
61
62
|
body: RequestBody,
|
|
62
63
|
globalSchemas: SchemasObject,
|
|
63
|
-
options:
|
|
64
|
+
options: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
|
|
64
65
|
): Promise<OperationArgs> {
|
|
65
66
|
let requestBodyIndex = -1;
|
|
66
67
|
if (operationSpec.requestBody) {
|
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
|
-
import {Context, inject, Provider} from '@loopback/core';
|
|
7
|
-
import {
|
|
6
|
+
import {bind, Context, inject, Provider} from '@loopback/core';
|
|
7
|
+
import {asMiddleware, Middleware} from '@loopback/express';
|
|
8
|
+
import debugFactory from 'debug';
|
|
8
9
|
import {HttpHandler} from '../http-handler';
|
|
9
|
-
import {RestBindings} from '../keys';
|
|
10
|
+
import {RestBindings, RestTags} from '../keys';
|
|
10
11
|
import {ResolvedRoute} from '../router';
|
|
12
|
+
import {RestMiddlewareGroups} from '../sequence';
|
|
13
|
+
import {FindRoute, Request} from '../types';
|
|
14
|
+
|
|
15
|
+
const debug = debugFactory('loopback:rest:find-route');
|
|
11
16
|
|
|
12
17
|
export class FindRouteProvider implements Provider<FindRoute> {
|
|
13
18
|
constructor(
|
|
@@ -21,7 +26,35 @@ export class FindRouteProvider implements Provider<FindRoute> {
|
|
|
21
26
|
|
|
22
27
|
action(request: Request): ResolvedRoute {
|
|
23
28
|
const found = this.handler.findRoute(request);
|
|
29
|
+
debug('Route found for %s %s', request.method, request.originalUrl, found);
|
|
24
30
|
found.updateBindings(this.context);
|
|
25
31
|
return found;
|
|
26
32
|
}
|
|
27
33
|
}
|
|
34
|
+
|
|
35
|
+
@bind(
|
|
36
|
+
asMiddleware({
|
|
37
|
+
group: RestMiddlewareGroups.FIND_ROUTE,
|
|
38
|
+
chain: RestTags.REST_MIDDLEWARE_CHAIN,
|
|
39
|
+
}),
|
|
40
|
+
)
|
|
41
|
+
export class FindRouteMiddlewareProvider implements Provider<Middleware> {
|
|
42
|
+
constructor(
|
|
43
|
+
@inject(RestBindings.SequenceActions.FIND_ROUTE)
|
|
44
|
+
protected findRoute: FindRoute,
|
|
45
|
+
) {}
|
|
46
|
+
|
|
47
|
+
value(): Middleware {
|
|
48
|
+
return async (ctx, next) => {
|
|
49
|
+
debug(
|
|
50
|
+
'Finding route for %s %s',
|
|
51
|
+
ctx.request.method,
|
|
52
|
+
ctx.request.originalUrl,
|
|
53
|
+
);
|
|
54
|
+
const route = this.findRoute(ctx.request);
|
|
55
|
+
debug('Route found', route);
|
|
56
|
+
ctx.bind(RestBindings.Operation.ROUTE).to(route);
|
|
57
|
+
return next();
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
|
-
import {Context, inject, Provider} from '@loopback/core';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
6
|
+
import {bind, Context, inject, Provider} from '@loopback/core';
|
|
7
|
+
import {asMiddleware, Middleware} from '@loopback/express';
|
|
8
|
+
import debugFactory from 'debug';
|
|
9
|
+
import {RestBindings, RestTags} from '../keys';
|
|
9
10
|
import {RouteEntry} from '../router';
|
|
11
|
+
import {RestMiddlewareGroups} from '../sequence';
|
|
12
|
+
import {InvokeMethod, OperationArgs, OperationRetval} from '../types';
|
|
13
|
+
|
|
14
|
+
const debug = debugFactory('loopback:rest:invoke-method');
|
|
10
15
|
|
|
11
16
|
export class InvokeMethodProvider implements Provider<InvokeMethod> {
|
|
12
17
|
constructor(@inject(RestBindings.Http.CONTEXT) protected context: Context) {}
|
|
@@ -19,3 +24,42 @@ export class InvokeMethodProvider implements Provider<InvokeMethod> {
|
|
|
19
24
|
return route.invokeHandler(this.context, args);
|
|
20
25
|
}
|
|
21
26
|
}
|
|
27
|
+
|
|
28
|
+
@bind(
|
|
29
|
+
asMiddleware({
|
|
30
|
+
group: RestMiddlewareGroups.INVOKE_METHOD,
|
|
31
|
+
upstreamGroups: RestMiddlewareGroups.PARSE_PARAMS,
|
|
32
|
+
chain: RestTags.REST_MIDDLEWARE_CHAIN,
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
export class InvokeMethodMiddlewareProvider implements Provider<Middleware> {
|
|
36
|
+
constructor(
|
|
37
|
+
@inject(RestBindings.SequenceActions.INVOKE_METHOD)
|
|
38
|
+
protected invokeMethod: InvokeMethod,
|
|
39
|
+
) {}
|
|
40
|
+
|
|
41
|
+
value(): Middleware {
|
|
42
|
+
return async (ctx, next) => {
|
|
43
|
+
const route: RouteEntry = await ctx.get(RestBindings.Operation.ROUTE);
|
|
44
|
+
const params: OperationArgs = await ctx.get(
|
|
45
|
+
RestBindings.Operation.PARAMS,
|
|
46
|
+
);
|
|
47
|
+
if (debug.enabled) {
|
|
48
|
+
debug('Invoking method %s with', route.describe(), params);
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const retVal = await this.invokeMethod(route, params);
|
|
52
|
+
ctx.bind(RestBindings.Operation.RETURN_VALUE).to(retVal);
|
|
53
|
+
if (debug.enabled) {
|
|
54
|
+
debug('Return value from %s', route.describe(), retVal);
|
|
55
|
+
}
|
|
56
|
+
return retVal;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (debug.enabled) {
|
|
59
|
+
debug('Error thrown from %s', route.describe(), err);
|
|
60
|
+
}
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
|
-
import {inject, Provider} from '@loopback/core';
|
|
6
|
+
import {bind, inject, Provider} from '@loopback/core';
|
|
7
|
+
import {asMiddleware, Middleware} from '@loopback/express';
|
|
8
|
+
import debugFactory from 'debug';
|
|
7
9
|
import {RequestBodyParser} from '../body-parsers';
|
|
8
|
-
import {RestBindings} from '../keys';
|
|
10
|
+
import {RestBindings, RestTags} from '../keys';
|
|
9
11
|
import {parseOperationArgs} from '../parser';
|
|
10
12
|
import {ResolvedRoute} from '../router';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
import {RestMiddlewareGroups} from '../sequence';
|
|
14
|
+
import {AjvFactory, ParseParams, Request, ValidationOptions} from '../types';
|
|
15
|
+
import {DEFAULT_AJV_VALIDATION_OPTIONS} from '../validation/ajv-factory.provider';
|
|
16
|
+
|
|
17
|
+
const debug = debugFactory('loopback:rest:parse-param');
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* Provides the function for parsing args in requests at runtime.
|
|
19
21
|
*
|
|
@@ -27,7 +29,7 @@ export class ParseParamsProvider implements Provider<ParseParams> {
|
|
|
27
29
|
RestBindings.REQUEST_BODY_PARSER_OPTIONS.deepProperty('validation'),
|
|
28
30
|
{optional: true},
|
|
29
31
|
)
|
|
30
|
-
private validationOptions:
|
|
32
|
+
private validationOptions: ValidationOptions = DEFAULT_AJV_VALIDATION_OPTIONS,
|
|
31
33
|
@inject(RestBindings.AJV_FACTORY, {optional: true})
|
|
32
34
|
private ajvFactory?: AjvFactory,
|
|
33
35
|
) {}
|
|
@@ -40,3 +42,28 @@ export class ParseParamsProvider implements Provider<ParseParams> {
|
|
|
40
42
|
});
|
|
41
43
|
}
|
|
42
44
|
}
|
|
45
|
+
|
|
46
|
+
@bind(
|
|
47
|
+
asMiddleware({
|
|
48
|
+
group: RestMiddlewareGroups.PARSE_PARAMS,
|
|
49
|
+
upstreamGroups: RestMiddlewareGroups.FIND_ROUTE,
|
|
50
|
+
chain: RestTags.REST_MIDDLEWARE_CHAIN,
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
export class ParseParamsMiddlewareProvider implements Provider<Middleware> {
|
|
54
|
+
constructor(
|
|
55
|
+
@inject(RestBindings.SequenceActions.PARSE_PARAMS)
|
|
56
|
+
protected parseParams: ParseParams,
|
|
57
|
+
) {}
|
|
58
|
+
|
|
59
|
+
value(): Middleware {
|
|
60
|
+
return async (ctx, next) => {
|
|
61
|
+
const route: ResolvedRoute = await ctx.get(RestBindings.Operation.ROUTE);
|
|
62
|
+
debug('Parsing parameters for %s %s', route.verb, route.path);
|
|
63
|
+
const params = await this.parseParams(ctx.request, route);
|
|
64
|
+
ctx.bind(RestBindings.Operation.PARAMS).to(params);
|
|
65
|
+
debug('Parameters', params);
|
|
66
|
+
return next();
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
// This file is licensed under the MIT License.
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {bind, inject, Provider} from '@loopback/core';
|
|
7
|
+
import {asMiddleware, Middleware} from '@loopback/express';
|
|
8
|
+
import {RestBindings, RestTags} from '../keys';
|
|
9
|
+
import {RestMiddlewareGroups} from '../sequence';
|
|
10
|
+
import {Reject, Send} from '../types';
|
|
7
11
|
import {writeResultToResponse} from '../writer';
|
|
8
12
|
/**
|
|
9
13
|
* Provides the function that populates the response object with
|
|
@@ -12,8 +16,47 @@ import {writeResultToResponse} from '../writer';
|
|
|
12
16
|
* @returns The handler function that will populate the
|
|
13
17
|
* response with operation results.
|
|
14
18
|
*/
|
|
15
|
-
export class SendProvider implements Provider<
|
|
19
|
+
export class SendProvider implements Provider<Send> {
|
|
16
20
|
value() {
|
|
17
21
|
return writeResultToResponse;
|
|
18
22
|
}
|
|
19
23
|
}
|
|
24
|
+
|
|
25
|
+
@bind(
|
|
26
|
+
asMiddleware({
|
|
27
|
+
group: RestMiddlewareGroups.SEND_RESPONSE,
|
|
28
|
+
downstreamGroups: [
|
|
29
|
+
RestMiddlewareGroups.CORS,
|
|
30
|
+
RestMiddlewareGroups.INVOKE_METHOD,
|
|
31
|
+
],
|
|
32
|
+
chain: RestTags.REST_MIDDLEWARE_CHAIN,
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
export class SendResponseMiddlewareProvider implements Provider<Middleware> {
|
|
36
|
+
constructor(
|
|
37
|
+
@inject(RestBindings.SequenceActions.SEND)
|
|
38
|
+
protected send: Send,
|
|
39
|
+
@inject(RestBindings.SequenceActions.REJECT)
|
|
40
|
+
protected reject: Reject,
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
value(): Middleware {
|
|
44
|
+
return async (ctx, next) => {
|
|
45
|
+
try {
|
|
46
|
+
/**
|
|
47
|
+
* Invoke downstream middleware to produce the result
|
|
48
|
+
*/
|
|
49
|
+
const result = await next();
|
|
50
|
+
/**
|
|
51
|
+
* Write the result to HTTP response
|
|
52
|
+
*/
|
|
53
|
+
this.send(ctx.response, result);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
/**
|
|
56
|
+
* Write the error to HTTP response
|
|
57
|
+
*/
|
|
58
|
+
this.reject(ctx, err);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/request-context.ts
CHANGED
|
@@ -17,7 +17,8 @@ import {RestServerResolvedConfig} from './rest.server';
|
|
|
17
17
|
* A per-request Context combining an IoC container with handler context
|
|
18
18
|
* (request, response, etc.).
|
|
19
19
|
*/
|
|
20
|
-
export class RequestContext
|
|
20
|
+
export class RequestContext
|
|
21
|
+
extends MiddlewareContext
|
|
21
22
|
implements HandlerContext {
|
|
22
23
|
/**
|
|
23
24
|
* Get the protocol used by the client to make the request.
|
package/src/rest-http-error.ts
CHANGED
|
@@ -11,7 +11,7 @@ export namespace RestHttpErrors {
|
|
|
11
11
|
name: string,
|
|
12
12
|
extraProperties?: Props,
|
|
13
13
|
): HttpErrors.HttpError & Props {
|
|
14
|
-
const msg = `Invalid data ${JSON.stringify(data)} for parameter ${name}
|
|
14
|
+
const msg = `Invalid data ${JSON.stringify(data)} for parameter "${name}".`;
|
|
15
15
|
return Object.assign(
|
|
16
16
|
new HttpErrors.BadRequest(msg),
|
|
17
17
|
{
|
|
@@ -51,11 +51,15 @@ export namespace RestHttpErrors {
|
|
|
51
51
|
|
|
52
52
|
export const INVALID_REQUEST_BODY_MESSAGE =
|
|
53
53
|
'The request body is invalid. See error object `details` property for more info.';
|
|
54
|
-
|
|
54
|
+
|
|
55
|
+
export function invalidRequestBody(
|
|
56
|
+
details: ValidationErrorDetails[],
|
|
57
|
+
): HttpErrors.HttpError & {details: ValidationErrorDetails[]} {
|
|
55
58
|
return Object.assign(
|
|
56
59
|
new HttpErrors.UnprocessableEntity(INVALID_REQUEST_BODY_MESSAGE),
|
|
57
60
|
{
|
|
58
61
|
code: 'VALIDATION_FAILED',
|
|
62
|
+
details,
|
|
59
63
|
},
|
|
60
64
|
);
|
|
61
65
|
}
|
package/src/rest.application.ts
CHANGED
|
@@ -102,7 +102,7 @@ export class RestApplication extends Application implements HttpServerLike {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
sequence(sequence: Constructor<SequenceHandler>): Binding {
|
|
105
|
-
return this.
|
|
105
|
+
return this.restServer.sequence(sequence);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
handler(handlerFn: SequenceFunction) {
|
package/src/rest.component.ts
CHANGED
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
// License text available at https://opensource.org/licenses/MIT
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
+
Application,
|
|
7
8
|
Binding,
|
|
9
|
+
Component,
|
|
8
10
|
Constructor,
|
|
11
|
+
CoreBindings,
|
|
12
|
+
CoreTags,
|
|
9
13
|
createBindingFromClass,
|
|
10
14
|
inject,
|
|
11
|
-
} from '@loopback/core';
|
|
12
|
-
import {
|
|
13
|
-
Application,
|
|
14
|
-
Component,
|
|
15
|
-
CoreBindings,
|
|
16
15
|
ProviderMap,
|
|
17
16
|
Server,
|
|
18
17
|
} from '@loopback/core';
|
|
@@ -26,21 +25,24 @@ import {
|
|
|
26
25
|
UrlEncodedBodyParser,
|
|
27
26
|
} from './body-parsers';
|
|
28
27
|
import {RawBodyParser} from './body-parsers/body-parser.raw';
|
|
29
|
-
import {RestBindings} from './keys';
|
|
28
|
+
import {RestBindings, RestTags} from './keys';
|
|
30
29
|
import {
|
|
30
|
+
FindRouteMiddlewareProvider,
|
|
31
31
|
FindRouteProvider,
|
|
32
|
+
InvokeMethodMiddlewareProvider,
|
|
32
33
|
InvokeMethodProvider,
|
|
33
34
|
LogErrorProvider,
|
|
35
|
+
ParseParamsMiddlewareProvider,
|
|
34
36
|
ParseParamsProvider,
|
|
35
37
|
RejectProvider,
|
|
36
38
|
SendProvider,
|
|
39
|
+
SendResponseMiddlewareProvider,
|
|
37
40
|
} from './providers';
|
|
38
41
|
import {
|
|
39
42
|
createBodyParserBinding,
|
|
40
43
|
RestServer,
|
|
41
44
|
RestServerConfig,
|
|
42
45
|
} from './rest.server';
|
|
43
|
-
import {DefaultSequence} from './sequence';
|
|
44
46
|
import {ConsolidationEnhancer} from './spec-enhancers/consolidate.spec-enhancer';
|
|
45
47
|
import {InfoSpecEnhancer} from './spec-enhancers/info.spec-enhancer';
|
|
46
48
|
import {AjvFactoryProvider} from './validation/ajv-factory.provider';
|
|
@@ -48,8 +50,6 @@ import {AjvFactoryProvider} from './validation/ajv-factory.provider';
|
|
|
48
50
|
export class RestComponent implements Component {
|
|
49
51
|
providers: ProviderMap = {
|
|
50
52
|
[RestBindings.SequenceActions.LOG_ERROR.key]: LogErrorProvider,
|
|
51
|
-
[RestBindings.SequenceActions.INVOKE_MIDDLEWARE
|
|
52
|
-
.key]: InvokeMiddlewareProvider,
|
|
53
53
|
[RestBindings.SequenceActions.FIND_ROUTE.key]: FindRouteProvider,
|
|
54
54
|
[RestBindings.SequenceActions.INVOKE_METHOD.key]: InvokeMethodProvider,
|
|
55
55
|
[RestBindings.SequenceActions.REJECT.key]: RejectProvider,
|
|
@@ -86,6 +86,8 @@ export class RestComponent implements Component {
|
|
|
86
86
|
),
|
|
87
87
|
createBindingFromClass(InfoSpecEnhancer),
|
|
88
88
|
createBindingFromClass(ConsolidationEnhancer),
|
|
89
|
+
|
|
90
|
+
...getRestMiddlewareBindings(),
|
|
89
91
|
];
|
|
90
92
|
servers: {
|
|
91
93
|
[name: string]: Constructor<Server>;
|
|
@@ -97,7 +99,26 @@ export class RestComponent implements Component {
|
|
|
97
99
|
@inject(CoreBindings.APPLICATION_INSTANCE) app: Application,
|
|
98
100
|
@inject(RestBindings.CONFIG) config?: RestComponentConfig,
|
|
99
101
|
) {
|
|
100
|
-
|
|
102
|
+
// Register the `InvokeMiddleware` with default to `ACTION_MIDDLEWARE_CHAIN`
|
|
103
|
+
// to keep backward compatibility with action based sequence
|
|
104
|
+
const invokeMiddlewareActionBinding = createBindingFromClass(
|
|
105
|
+
InvokeMiddlewareProvider,
|
|
106
|
+
{
|
|
107
|
+
key: RestBindings.SequenceActions.INVOKE_MIDDLEWARE,
|
|
108
|
+
},
|
|
109
|
+
).tag({[CoreTags.EXTENSION_POINT]: RestTags.ACTION_MIDDLEWARE_CHAIN});
|
|
110
|
+
app.add(invokeMiddlewareActionBinding);
|
|
111
|
+
|
|
112
|
+
// Register the `InvokeMiddleware` with default to `DEFAULT_MIDDLEWARE_CHAIN`
|
|
113
|
+
// for the middleware based sequence
|
|
114
|
+
const invokeMiddlewareServiceBinding = createBindingFromClass(
|
|
115
|
+
InvokeMiddlewareProvider,
|
|
116
|
+
{
|
|
117
|
+
key: RestBindings.INVOKE_MIDDLEWARE_SERVICE,
|
|
118
|
+
},
|
|
119
|
+
).tag({[CoreTags.EXTENSION_POINT]: RestTags.REST_MIDDLEWARE_CHAIN});
|
|
120
|
+
app.add(invokeMiddlewareServiceBinding);
|
|
121
|
+
|
|
101
122
|
const apiSpec = createEmptyApiSpec();
|
|
102
123
|
// Merge the OpenAPI `servers` spec from the config into the empty one
|
|
103
124
|
if (config?.openApiSpec?.servers) {
|
|
@@ -107,5 +128,14 @@ export class RestComponent implements Component {
|
|
|
107
128
|
}
|
|
108
129
|
}
|
|
109
130
|
|
|
131
|
+
function getRestMiddlewareBindings() {
|
|
132
|
+
return [
|
|
133
|
+
SendResponseMiddlewareProvider,
|
|
134
|
+
FindRouteMiddlewareProvider,
|
|
135
|
+
ParseParamsMiddlewareProvider,
|
|
136
|
+
InvokeMethodMiddlewareProvider,
|
|
137
|
+
].map(cls => createBindingFromClass(cls));
|
|
138
|
+
}
|
|
139
|
+
|
|
110
140
|
// TODO(kevin): Extend this interface def to include multiple servers?
|
|
111
141
|
export type RestComponentConfig = RestServerConfig;
|