express(selectedMiddlewares = ALL_MIDDLEWARES) {
        if (!Array.isArray(selectedMiddlewares)) {
            selectedMiddlewares = [selectedMiddlewares];
        }

        let expressRouter = Router;
        let {router: {base}, build: {ssr, publicPath}, serviceWorker, errorHandler} = this.config;
        base = removeTrailingSlash(base || '/');

        if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.FAVICON)) {
            // serve favicon
            let faviconPath = posix.join(this.cwd, ASSETS_DIRNAME_IN_DIST, 'img/icons/favicon.ico');
            this.add(favicon(faviconPath));
        }

        if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.TRAILING_SLASH)) {
            // Redirect without trailing slash.
            let rootRouter = expressRouter();
            rootRouter.get(
                base,
                (req, res, next) => {
                    let url = parse(req.url);
                    if (!url.pathname.endsWith('/')) {
                        res.redirect(301, url.pathname + '/' + (url.search || ''));
                    }
                    else {
                        next();
                    }
                }
            );
            this.add(rootRouter, true);
        }

        if (ssr) {
            /**
             * Add static files middleware only in prod mode,
             * because we already have webpack-dev-middleware in dev mode.
             * Don't need this middleware when CDN being used to serve static files.
             */
            if (this.isProd && !isFromCDN(publicPath)) {
                // Serve /static.
                if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.STATIC)) {
                    let staticRouter = expressRouter();
                    staticRouter.get(
                        posix.join(publicPath, ASSETS_DIRNAME_IN_DIST, '*'),
                        staticFactory(publicPath)
                    );
                    this.add(staticRouter);
                    // Don't use etag or cache-control.
                    this.add(serveStatic(this.cwd, {
                        cacheControl: false,
                        etag: false
                    }));
                }

                // Serve sw-register.js & sw.js.
                if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.SERVICE_WORKER)) {
                    if (serviceWorker && serviceWorker.swDest) {
                        let swFiles = [
                            basename(serviceWorker.swDest),
                            'sw-register.js'
                        ].map(f => posix.join(publicPath, f));
                        let swRouter = expressRouter();
                        swRouter.get(swFiles, staticFactory(publicPath));
                        this.add(swRouter);
                        // Use cache-control but not etag.
                        this.add(serveStatic(this.cwd, {
                            etag: false
                        }));
                    }
                }
            }
            
            this.add(contentTypeFactory());

            // SSR middleware.
            if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.SSR)) {
                this.add(ssrFactory(this.core));
            }
        }

        // Handle errors.
        if (selectedMiddlewares.includes(INTERNAL_MIDDLEWARE.ERROR)) {
            this.add(expressErrorFactory(errorHandler));
        }

        return compose(this.internalMiddlewares);
    }
compose middlewares into a chain.

@param {Array<Function>|Function} selectedMiddlewares middlewares selected by user
@return {Function} expressMiddleware