diff --git a/packages/rest/package.json b/packages/rest/package.json index 099aa544b316..42df7a3d5c61 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -24,6 +24,7 @@ "@loopback/http-server": "^1.4.0", "@loopback/openapi-v3": "^1.6.1", "@loopback/openapi-v3-types": "^1.1.1", + "@loopback/express-middleware": "^1.0.0-1", "@types/body-parser": "^1.17.0", "@types/cors": "^2.8.5", "@types/express": "^4.17.0", diff --git a/packages/rest/src/rest.server.ts b/packages/rest/src/rest.server.ts index 8ff56b3d817a..b7d41121be61 100644 --- a/packages/rest/src/rest.server.ts +++ b/packages/rest/src/rest.server.ts @@ -12,6 +12,10 @@ import { inject, } from '@loopback/context'; import {Application, CoreBindings, Server} from '@loopback/core'; +import { + ExpressBindings, + MiddlewareRegistry, +} from '@loopback/express-middleware'; import {HttpServer, HttpServerOptions} from '@loopback/http-server'; import {getControllerSpec} from '@loopback/openapi-v3'; import { @@ -221,24 +225,50 @@ export class RestServer extends Context implements Server, HttpServerLike { this._applyExpressSettings(); this._requestHandler = this._expressApp; + if (!this.isBound(ExpressBindings.EXPRESS_MIDDLEWARE_REGISTRY)) { + // Set up the default express middleware registry + this.bind(ExpressBindings.EXPRESS_MIDDLEWARE_REGISTRY).toClass( + MiddlewareRegistry, + ); + } + const middlewareRegistry = this.getSync( + ExpressBindings.EXPRESS_MIDDLEWARE_REGISTRY, + ); + middlewareRegistry.setMiddlewareRegistryOptions({ + phasesByOrder: ['cors', 'openapi-spec', 'rest'], + }); + // Allow CORS support for all endpoints so that users // can test with online SwaggerUI instance - this._expressApp.use(cors(this.config.cors)); + middlewareRegistry.middleware(cors(this.config.cors), { + phase: 'cors', + name: 'cors', + }); // Set up endpoints for OpenAPI spec/ui - this._setupOpenApiSpecEndpoints(); + this._setupOpenApiSpecEndpoints(middlewareRegistry); // Mount our router & request handler - this._expressApp.use(this._basePath, (req, res, next) => { - this._handleHttpRequest(req, res).catch(next); - }); + middlewareRegistry.middleware( + (req, res, next) => { + this._handleHttpRequest(req, res).catch(next); + }, + { + path: this._basePath, + phase: 'rest', + name: 'rest', + }, + ); // Mount our error handler - this._expressApp.use( + middlewareRegistry.errorMiddleware( (err: Error, req: Request, res: Response, next: Function) => { this._onUnhandledError(req, res, err); }, + {name: 'error'}, ); + + this._expressApp.use(middlewareRegistry.requestHandler); } /** @@ -258,7 +288,7 @@ export class RestServer extends Context implements Server, HttpServerLike { * Mount /openapi.json, /openapi.yaml for specs and /swagger-ui, /explorer * to redirect to externally hosted API explorer */ - protected _setupOpenApiSpecEndpoints() { + protected _setupOpenApiSpecEndpoints(middlewareRegistry: MiddlewareRegistry) { if (this.config.openApiSpec.disabled) return; // NOTE(bajtos) Regular routes are handled through Sequence. // IMO, this built-in endpoint should not run through a Sequence, @@ -270,14 +300,24 @@ export class RestServer extends Context implements Server, HttpServerLike { const mapping = this.config.openApiSpec.endpointMapping!; // Serving OpenAPI spec for (const p in mapping) { - this._expressApp.get(p, (req, res) => - this._serveOpenApiSpec(req, res, mapping[p]), + middlewareRegistry.middleware( + (req, res) => this._serveOpenApiSpec(req, res, mapping[p]), + { + path: p, + method: 'get', + phase: 'openapi-spec', + }, ); } const explorerPaths = ['/swagger-ui', '/explorer']; - this._expressApp.get(explorerPaths, (req, res, next) => - this._redirectToSwaggerUI(req, res, next), + middlewareRegistry.middleware( + (req, res, next) => this._redirectToSwaggerUI(req, res, next), + { + path: explorerPaths, + method: 'get', + phase: 'openapi-spec', + }, ); }