From 2b2d596e3fbe8583418129289bca6ae6a45e3637 Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Tue, 20 Jun 2023 23:24:50 +0200 Subject: [PATCH] fix(router): send 204 with empty string in preemptive mode instead of 404 (#409) --- src/app.ts | 2 +- src/router.ts | 23 +++++++++++++++++------ test/router.test.ts | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/app.ts b/src/app.ts index 475f49aa..f49da5ad 100644 --- a/src/app.ts +++ b/src/app.ts @@ -143,7 +143,7 @@ export function createAppEventHandler(stack: Stack, options: AppOptions) { if (!event.node.res.writableEnded) { throw createError({ statusCode: 404, - statusMessage: `Cannot find any route matching ${ + statusMessage: `Cannot find any path matching ${ event.node.req.url || "/" }.`, }); diff --git a/src/router.ts b/src/router.ts index 063c7483..5a5d6b6f 100644 --- a/src/router.ts +++ b/src/router.ts @@ -2,6 +2,7 @@ import { createRouter as _createRouter } from "radix3"; import type { HTTPMethod, EventHandler } from "./types"; import { createError } from "./error"; import { eventHandler, toEventHandler } from "./event"; +import { setResponseStatus } from "./utils"; export type RouterMethod = Lowercase; const RouterMethods: RouterMethod[] = [ @@ -103,11 +104,15 @@ export function createRouter(opts: CreateRouterOptions = {}): Router { ).toLowerCase() as RouterMethod; const handler = matched.handlers[method] || matched.handlers.all; if (!handler) { - throw createError({ - statusCode: 405, - name: "Method Not Allowed", - statusMessage: `Method ${method} is not allowed on this route.`, - }); + if (opts.preemptive || opts.preemtive) { + throw createError({ + statusCode: 405, + name: "Method Not Allowed", + statusMessage: `Method ${method} is not allowed on this route.`, + }); + } else { + return; // Let app match other handlers + } } // Add params @@ -115,7 +120,13 @@ export function createRouter(opts: CreateRouterOptions = {}): Router { event.context.params = params; // Call handler - return handler(event); + return Promise.resolve(handler(event)).then((res) => { + if (res === undefined && (opts.preemptive || opts.preemtive)) { + setResponseStatus(event, 204); + return ""; + } + return res; + }); }); return router; diff --git a/test/router.test.ts b/test/router.test.ts index 651444d0..6a1f9975 100644 --- a/test/router.test.ts +++ b/test/router.test.ts @@ -97,11 +97,50 @@ describe("router", () => { const res = await request.get("/404"); expect(res.status).toEqual(404); }); +}); + +describe("router (preemptive)", () => { + let app: App; + let router: Router; + let request: SuperTest; + + beforeEach(() => { + app = createApp({ debug: false }); + router = createRouter({ preemptive: true }) + .get( + "/test", + eventHandler(() => "Test") + ) + .get( + "/undefined", + eventHandler(() => undefined) + ); + app.use(router); + request = supertest(toNodeListener(app)); + }); + + it("Handle /test", async () => { + const res = await request.get("/test"); + expect(res.text).toEqual("Test"); + }); + + it("Handle /404", async () => { + const res = await request.get("/404"); + expect(JSON.parse(res.text)).toMatchObject({ + statusCode: 404, + statusMessage: "Cannot find any route matching /404.", + }); + }); it("Not matching route method", async () => { const res = await request.head("/test"); expect(res.status).toEqual(405); }); + + it("Handle /undefined", async () => { + const res = await request.get("/undefined"); + expect(res.text).toEqual(""); + }); }); describe("getRouterParams", () => {