UNPKG

@tsed/platform-http

Version:
106 lines (105 loc) 4.09 kB
import { AnyToPromiseStatus, catchAsyncError } from "@tsed/core"; import { inject, injectable, ProviderScope } from "@tsed/di"; import { $alter } from "@tsed/hooks"; import { PlatformExceptions } from "@tsed/platform-exceptions"; import { PlatformParams } from "@tsed/platform-params"; import { PlatformResponseFilter } from "@tsed/platform-response-filter"; import { PlatformHandlerMetadata, PlatformHandlerType, PlatformRouters, useResponseHandler } from "@tsed/platform-router"; import { AnyToPromiseWithCtx } from "../domain/AnyToPromiseWithCtx.js"; import { setResponseHeaders } from "../utils/setResponseHeaders.js"; import { PlatformAdapter } from "./PlatformAdapter.js"; /** * Platform Handler abstraction layer. Wrap original class method to a pure platform handler (Express, Koa, etc...). * @platform */ export class PlatformHandler { constructor() { this.responseFilter = inject(PlatformResponseFilter); this.platformParams = inject(PlatformParams); this.platformExceptions = inject(PlatformExceptions); this.platformRouters = inject(PlatformRouters); // configure the router module this.platformRouters.hooks .on("alterEndpointHandlers", (handlers, operationRoute) => { handlers = $alter("$alterEndpointHandlers", handlers, [operationRoute]); handlers.after.push(useResponseHandler(this.flush.bind(this))); return handlers; }) .on("alterHandler", (handlerMetadata) => { const handler = handlerMetadata.isInjectable() ? this.createHandler(handlerMetadata) : handlerMetadata.handler; handlerMetadata.compiledHandler = handler; return inject(PlatformAdapter).mapHandler(handler, handlerMetadata); }); } createHandler(handlerMetadata) { const handler = this.platformParams.compileHandler(handlerMetadata); return ($ctx) => { $ctx.handlerMetadata = handlerMetadata; return this.onRequest(handler, $ctx); }; } /** * @param provider * @param propertyKey */ createCustomHandler(provider, propertyKey) { const metadata = new PlatformHandlerMetadata({ provider, type: PlatformHandlerType.CUSTOM, propertyKey }); return this.createHandler(metadata); } /** * Call handler when a request his handle */ async onRequest(handler, $ctx) { const { handlerMetadata } = $ctx; if (handlerMetadata.type === PlatformHandlerType.CTX_FN) { return handler({ $ctx }); } const resolver = new AnyToPromiseWithCtx($ctx); const response = await resolver.call(handler); // Note: restore previous handler metadata (for OIDC) $ctx.handlerMetadata = handlerMetadata; if (response.state === AnyToPromiseStatus.RESOLVED && !$ctx.isDone()) { return this.onResponse(response, $ctx); } } onResponse({ status, data, headers }, $ctx) { if (status) { $ctx.response.status(status); } if (headers) { $ctx.response.setHeaders(headers); } if (data !== undefined) { $ctx.data = data; } $ctx.error = null; // set headers each times that an endpoint is called if ($ctx.handlerMetadata.isEndpoint()) { setResponseHeaders($ctx); } } /** * Send the response to the consumer. * @protected * @param $ctx */ async flush($ctx) { if (!$ctx.error) { $ctx.error = await catchAsyncError(async () => { const { response } = $ctx; if (!$ctx.isDone()) { const data = await this.responseFilter.transform($ctx.data, $ctx); response.body(data); } }); } if ($ctx.error) { this.platformExceptions.catch($ctx.error, $ctx); } } } injectable(PlatformHandler).scope(ProviderScope.SINGLETON);