backend-common: reimplement middleware using MiddlewareFactory
Co-authored-by: Fredrik Adelöw <freben@gmail.com> Co-authored-by: blam <ben@blam.sh> Co-authored-by: Johan Haals <johan.haals@gmail.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -20,5 +20,6 @@
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './lib/http';
|
||||
export * from './wiring';
|
||||
export * from './services/implementations';
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"test:kubernetes": "backstage-cli package test -t KubernetesContainerRunner --no-watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-app-api": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/cli-common": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
|
||||
@@ -14,19 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AuthenticationError,
|
||||
ConflictError,
|
||||
ErrorResponseBody,
|
||||
InputError,
|
||||
NotAllowedError,
|
||||
NotFoundError,
|
||||
NotModifiedError,
|
||||
serializeError,
|
||||
} from '@backstage/errors';
|
||||
import { ErrorRequestHandler, NextFunction, Request, Response } from 'express';
|
||||
import { ErrorRequestHandler } from 'express';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { getRootLogger } from '../logging';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { MiddlewareFactory } from '@backstage/backend-app-api';
|
||||
|
||||
/**
|
||||
* Options passed to the {@link errorHandler} middleware.
|
||||
@@ -73,69 +65,11 @@ export type ErrorHandlerOptions = {
|
||||
export function errorHandler(
|
||||
options: ErrorHandlerOptions = {},
|
||||
): ErrorRequestHandler {
|
||||
const showStackTraces =
|
||||
options.showStackTraces ?? process.env.NODE_ENV === 'development';
|
||||
|
||||
const logger = (options.logger || getRootLogger()).child({
|
||||
type: 'errorHandler',
|
||||
return MiddlewareFactory.create({
|
||||
config: new ConfigReader({}),
|
||||
logger: options.logger ?? getRootLogger(),
|
||||
}).error({
|
||||
logAllErrors: options.logClientErrors,
|
||||
showStackTraces: options.showStackTraces,
|
||||
});
|
||||
|
||||
return (error: Error, req: Request, res: Response, next: NextFunction) => {
|
||||
const statusCode = getStatusCode(error);
|
||||
if (options.logClientErrors || statusCode >= 500) {
|
||||
logger.error(`Request failed with status ${statusCode}`, error);
|
||||
}
|
||||
|
||||
if (res.headersSent) {
|
||||
// If the headers have already been sent, do not send the response again
|
||||
// as this will throw an error in the backend.
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const body: ErrorResponseBody = {
|
||||
error: serializeError(error, { includeStack: showStackTraces }),
|
||||
request: { method: req.method, url: req.url },
|
||||
response: { statusCode },
|
||||
};
|
||||
|
||||
res.status(statusCode).json(body);
|
||||
};
|
||||
}
|
||||
|
||||
function getStatusCode(error: Error): number {
|
||||
// Look for common http library status codes
|
||||
const knownStatusCodeFields = ['statusCode', 'status'];
|
||||
for (const field of knownStatusCodeFields) {
|
||||
const statusCode = (error as any)[field];
|
||||
if (
|
||||
typeof statusCode === 'number' &&
|
||||
(statusCode | 0) === statusCode && // is whole integer
|
||||
statusCode >= 100 &&
|
||||
statusCode <= 599
|
||||
) {
|
||||
return statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle well-known error types
|
||||
switch (error.name) {
|
||||
case NotModifiedError.name:
|
||||
return 304;
|
||||
case InputError.name:
|
||||
return 400;
|
||||
case AuthenticationError.name:
|
||||
return 401;
|
||||
case NotAllowedError.name:
|
||||
return 403;
|
||||
case NotFoundError.name:
|
||||
return 404;
|
||||
case ConflictError.name:
|
||||
return 409;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Fall back to internal server error
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
||||
import { MiddlewareFactory } from '@backstage/backend-app-api';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { RequestHandler } from 'express';
|
||||
import { getRootLogger } from '../logging';
|
||||
|
||||
/**
|
||||
* Express middleware to handle requests for missing routes.
|
||||
@@ -26,8 +29,8 @@ import { NextFunction, Request, RequestHandler, Response } from 'express';
|
||||
* @returns An Express request handler
|
||||
*/
|
||||
export function notFoundHandler(): RequestHandler {
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
return (_request: Request, response: Response, _next: NextFunction) => {
|
||||
response.status(404).end();
|
||||
};
|
||||
return MiddlewareFactory.create({
|
||||
config: new ConfigReader({}),
|
||||
logger: getRootLogger(),
|
||||
}).notFound();
|
||||
}
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MiddlewareFactory } from '@backstage/backend-app-api';
|
||||
import { RequestHandler } from 'express';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import morgan from 'morgan';
|
||||
import { getRootLogger } from '../logging';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
|
||||
/**
|
||||
* Logs incoming requests.
|
||||
@@ -27,15 +28,8 @@ import { getRootLogger } from '../logging';
|
||||
* @returns An Express request handler
|
||||
*/
|
||||
export function requestLoggingHandler(logger?: LoggerService): RequestHandler {
|
||||
const actualLogger = (logger || getRootLogger()).child({
|
||||
type: 'incomingRequest',
|
||||
});
|
||||
|
||||
return morgan('combined', {
|
||||
stream: {
|
||||
write(message: string) {
|
||||
actualLogger.info(message.trimEnd());
|
||||
},
|
||||
},
|
||||
});
|
||||
return MiddlewareFactory.create({
|
||||
config: new ConfigReader({}),
|
||||
logger: logger ?? getRootLogger(),
|
||||
}).logging();
|
||||
}
|
||||
|
||||
@@ -3413,6 +3413,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/backend-common@workspace:packages/backend-common"
|
||||
dependencies:
|
||||
"@backstage/backend-app-api": "workspace:^"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user