From e9cc630bdfe172f3bacfade4724d1bcfea226d69 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Wed, 6 Mar 2019 15:09:23 -0800 Subject: [PATCH] feat(rest): use @loopback/express-middleware to manage express middleware --- packages/rest/package.json | 1 + packages/rest/src/keys.ts | 29 ++++++++------- packages/rest/src/rest.server.ts | 60 +++++++++++++++++++++++--------- 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/packages/rest/package.json b/packages/rest/package.json index d97ab94fac24..fad107560332 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -23,6 +23,7 @@ "@loopback/context": "^1.6.0", "@loopback/core": "^1.1.7", "@loopback/http-server": "^1.1.7", + "@loopback/express-middleware": "^1.0.0-1", "@loopback/openapi-v3": "^1.2.3", "@loopback/openapi-v3-types": "^1.0.7", "@types/body-parser": "^1.17.0", diff --git a/packages/rest/src/keys.ts b/packages/rest/src/keys.ts index 3a35b3461173..fd7251f8100d 100644 --- a/packages/rest/src/keys.ts +++ b/packages/rest/src/keys.ts @@ -3,16 +3,20 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {CoreBindings} from '@loopback/core'; import {BindingKey, Context} from '@loopback/context'; - +import {CoreBindings} from '@loopback/core'; +import {ExpressContext} from '@loopback/express-middleware'; +import {HttpProtocol} from '@loopback/http-server'; +import * as https from 'https'; /** * See https://github.com/Microsoft/TypeScript/issues/26985 */ // import {OpenApiSpec} from '@loopback/openapi-v3-types'; import {OpenAPIObject as OpenApiSpec} from 'openapi3-ts'; - +import {ErrorWriterOptions} from 'strong-error-handler'; +import {BodyParser, RequestBodyParser} from './body-parsers'; import {HttpHandler} from './http-handler'; +import {RestRouter} from './router'; import {SequenceHandler} from './sequence'; import { BindElement, @@ -20,20 +24,14 @@ import { GetFromContext, InvokeMethod, LogError, - Request, - Response, ParseParams, Reject, - Send, + Request, RequestBodyParserOptions, + Response, + Send, } from './types'; -import {HttpProtocol} from '@loopback/http-server'; -import * as https from 'https'; -import {ErrorWriterOptions} from 'strong-error-handler'; -import {RestRouter} from './router'; -import {RequestBodyParser, BodyParser} from './body-parsers'; - /** * RestServer-specific bindings */ @@ -80,6 +78,13 @@ export namespace RestBindings { */ export const ROUTER = BindingKey.create('rest.router'); + /** + * Binding key for rest express context + */ + export const EXPRESS_CONTEXT = BindingKey.create( + 'rest.express.context', + ); + /** * Binding key for setting and injecting Reject action's error handling * options. diff --git a/packages/rest/src/rest.server.ts b/packages/rest/src/rest.server.ts index 38eaf5065f0c..5b767e5d1aed 100644 --- a/packages/rest/src/rest.server.ts +++ b/packages/rest/src/rest.server.ts @@ -5,13 +5,14 @@ import { Binding, + BindingAddress, + BindingScope, Constructor, Context, inject, - BindingScope, - BindingAddress, } from '@loopback/context'; import {Application, CoreBindings, Server} from '@loopback/core'; +import {ExpressContext} from '@loopback/express-middleware'; import {HttpServer, HttpServerOptions} from '@loopback/http-server'; import {getControllerSpec} from '@loopback/openapi-v3'; import { @@ -38,11 +39,11 @@ import { ControllerInstance, ControllerRoute, createControllerFactoryForBinding, + ExternalExpressRoutes, + RedirectRoute, Route, RouteEntry, RoutingTable, - ExternalExpressRoutes, - RedirectRoute, } from './router'; import {DefaultSequence, SequenceFunction, SequenceHandler} from './sequence'; import { @@ -51,9 +52,9 @@ import { ParseParams, Reject, Request, + RequestBodyParserOptions, Response, Send, - RequestBodyParserOptions, } from './types'; const debug = debugFactory('loopback:rest:server'); @@ -218,6 +219,12 @@ export class RestServer extends Context implements Server, HttpServerLike { this._applyExpressSettings(); this._requestHandler = this._expressApp; + const expressCtx = new ExpressContext(this); + expressCtx.setMiddlewareRegistryOptions({ + phasesByOrder: ['cors', 'openapi-spec', 'rest'], + }); + this.bind(RestBindings.EXPRESS_CONTEXT).to(expressCtx); + // Allow CORS support for all endpoints so that users // can test with online SwaggerUI instance const corsOptions = this.config.cors || { @@ -228,22 +235,33 @@ export class RestServer extends Context implements Server, HttpServerLike { maxAge: 86400, credentials: true, }; - this._expressApp.use(cors(corsOptions)); + + expressCtx.middleware(cors(corsOptions), {phase: 'cors', name: 'cors'}); // Set up endpoints for OpenAPI spec/ui - this._setupOpenApiSpecEndpoints(); + this._setupOpenApiSpecEndpoints(expressCtx); // Mount our router & request handler - this._expressApp.use(this._basePath, (req, res, next) => { - this._handleHttpRequest(req, res).catch(next); - }); + expressCtx.middleware( + (req, res, next) => { + this._handleHttpRequest(req, res).catch(next); + }, + { + path: this._basePath, + phase: 'rest', + name: 'rest', + }, + ); // Mount our error handler - this._expressApp.use( + expressCtx.errorMiddleware( (err: Error, req: Request, res: Response, next: Function) => { this._onUnhandledError(req, res, err); }, + {name: 'error'}, ); + + this._expressApp.use(expressCtx.requestHandler); } /** @@ -260,7 +278,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(expressCtx: ExpressContext) { 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, @@ -272,14 +290,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]), + expressCtx.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), + expressCtx.middleware( + (req, res, next) => this._redirectToSwaggerUI(req, res, next), + { + path: explorerPaths, + method: 'get', + phase: 'openapi-spec', + }, ); }