feat(http): add error handling for invalid HTTP methods
- Introduce `InvalidHttpMethodError` for unrecognized HTTP methods. - Enhance type safety in `HttpKernel` by using generic contexts. - Update `ResponseDecorator` to accept context for enriched responses. Signed-off-by: Max P. <Mail@MPassarello.de>
This commit is contained in:
25
src/Errors/InvalidHttpMethodError.ts
Normal file
25
src/Errors/InvalidHttpMethodError.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Represents an error thrown when an incoming HTTP method
|
||||
* is not among the recognized set of valid HTTP methods.
|
||||
*
|
||||
* This is typically used in routers or request dispatchers
|
||||
* to enforce allowed methods and produce 405-like behavior.
|
||||
*/
|
||||
export class InvalidHttpMethodError extends Error {
|
||||
/**
|
||||
* The invalid method that triggered this error.
|
||||
*/
|
||||
public readonly method: unknown;
|
||||
|
||||
/**
|
||||
* A fixed HTTP status code representing "Method Not Allowed".
|
||||
*/
|
||||
public readonly status: number = 405;
|
||||
|
||||
constructor(method: unknown) {
|
||||
const label = typeof method === 'string' ? method : '[non-string]';
|
||||
super(`Unsupported HTTP method: ${label}`);
|
||||
this.name = 'InvalidHttpMethodError';
|
||||
this.method = method;
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ export class HttpKernel<TContext extends IContext = IContext>
|
||||
/**
|
||||
* The list of internally registered routes, each with method, matcher, middleware, and handler.
|
||||
*/
|
||||
private routes: IInternalRoute[] = [];
|
||||
private routes: IInternalRoute<TContext>[] = [];
|
||||
|
||||
/**
|
||||
* Creates a new instance of the `HttpKernel`.
|
||||
@@ -71,10 +71,10 @@ export class HttpKernel<TContext extends IContext = IContext>
|
||||
query: parseQuery(url.searchParams),
|
||||
state: {},
|
||||
} as _TContext;
|
||||
return await this.executePipeline(
|
||||
return await this.executePipeline<_TContext>(
|
||||
ctx,
|
||||
route.middlewares,
|
||||
route.handler,
|
||||
route.middlewares as unknown as IMiddleware<_TContext>[],
|
||||
route.handler as unknown as IHandler<_TContext>,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -89,8 +89,10 @@ export class HttpKernel<TContext extends IContext = IContext>
|
||||
*
|
||||
* @param route - The fully constructed route including matcher, middlewares, and handler.
|
||||
*/
|
||||
private registerRoute(route: IInternalRoute): void {
|
||||
this.routes.push(route);
|
||||
private registerRoute<_TContext extends IContext = TContext>(
|
||||
route: IInternalRoute<_TContext>,
|
||||
): void {
|
||||
this.routes.push(route as unknown as IInternalRoute<TContext>);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,23 +109,22 @@ export class HttpKernel<TContext extends IContext = IContext>
|
||||
* @param handler - The final request handler to invoke at the end of the pipeline.
|
||||
* @returns The final HTTP response after middleware and decoration.
|
||||
*/
|
||||
private async executePipeline(
|
||||
ctx: IContext,
|
||||
middleware: IMiddleware[],
|
||||
handler: IHandler,
|
||||
private async executePipeline<_TContext extends IContext = TContext>(
|
||||
ctx: _TContext,
|
||||
middleware: IMiddleware<_TContext>[],
|
||||
handler: IHandler<_TContext>,
|
||||
): Promise<Response> {
|
||||
let i = -1;
|
||||
const dispatch = async (index: number): Promise<Response> => {
|
||||
if (index <= i) throw new Error('next() called multiple times');
|
||||
i = index;
|
||||
const fn: IMiddleware | IHandler = index < middleware.length
|
||||
? middleware[index]
|
||||
: handler;
|
||||
const fn: IMiddleware<_TContext> | IHandler<_TContext> =
|
||||
index < middleware.length ? middleware[index] : handler;
|
||||
if (!fn) return new Response('Internal error', { status: 500 });
|
||||
return index < middleware.length
|
||||
? await fn(ctx, () => dispatch(index + 1))
|
||||
: await (fn as IHandler)(ctx);
|
||||
: await (fn as IHandler<_TContext>)(ctx);
|
||||
};
|
||||
return this.decorateResponse(await dispatch(0));
|
||||
return this.decorateResponse(await dispatch(0), ctx);
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { IContext } from '../Interfaces/mod.ts';
|
||||
|
||||
/**
|
||||
* A function that modifies or enriches an outgoing HTTP response before it is returned to the client.
|
||||
*
|
||||
@@ -22,4 +24,7 @@
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
export type ResponseDecorator = (res: Response) => Response;
|
||||
export type ResponseDecorator<TContext extends IContext = IContext> = (
|
||||
res: Response,
|
||||
ctx: TContext,
|
||||
) => Response;
|
||||
|
Reference in New Issue
Block a user