From bb1d0db57a18994d201ac5da1b8906a57a1ff954 Mon Sep 17 00:00:00 2001 From: dk <2597375+Thesephi@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:05:15 +0100 Subject: [PATCH] v0.12.1 - see CHANGELOG for details (#37) --- CHANGELOG.md | 8 ++++ README.md | 16 ++++---- jsr.json | 2 +- src/ControllerMethodArgs.ts | 8 ++-- src/ControllerMethodArgs_test.ts | 63 ++++++++++++++++++++------------ 5 files changed, 62 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faebc00..07d7716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.12.1] - 2024-11-05 + +### Changed + +- laxed parsing rule for requests with header 'content-type: application/json' + now covers all 3 methods: GET, DELETE, and HEAD +- doc updated in README + ## [0.12.0] - 2024-11-05 ### Changed diff --git a/README.md b/README.md index 8f97bc4..1ded138 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,13 @@ deno add @oak/oak @dklab/oak-routing-ctrl ```ts // main.ts -import { Application } from "@oak/oak/application"; +import { Application } from "jsr:@oak/oak/application"; import { Controller, ControllerMethodArgs, Get, useOakServer, -} from "@dklab/oak-routing-ctrl"; +} from "jsr:@dklab/oak-routing-ctrl"; const app = new Application(); @@ -96,13 +96,13 @@ curl localhost:1993/v1/hello/world # prints: hello, world View Example ```ts -import { Application } from "@oak/oak/application"; +import { Application } from "jsr:@oak/oak/application"; import { Controller, ControllerMethodArgs, Post, useOakServer, -} from "@dklab/oak-routing-ctrl"; +} from "jsr:@dklab/oak-routing-ctrl"; @Controller("/v1") class MyController { @@ -133,13 +133,13 @@ curl -H"Content-Type: application/json" localhost:1993/v1/tell/alice -d'{"messag View Example ```ts -import { Application } from "@oak/oak/application"; +import { Application } from "jsr:@oak/oak/application"; import { Controller, ControllerMethodArgs, Get, useOakServer, -} from "@dklab/oak-routing-ctrl"; +} from "jsr:@dklab/oak-routing-ctrl"; @Controller("/v1") class MyController { @@ -170,8 +170,8 @@ curl localhost:1993/v1/books/thriller\?page=2 View Example ```ts -import { Application } from "@oak/oak/application"; -import { Controller, Get, useOakServer } from "@dklab/oak-routing-ctrl"; +import { Application } from "jsr:@oak/oak/application"; +import { Controller, Get, useOakServer } from "jsr:@dklab/oak-routing-ctrl"; @Controller() class MyController { diff --git a/jsr.json b/jsr.json index 196c8ca..48b77b9 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@dklab/oak-routing-ctrl", - "version": "0.12.0", + "version": "0.12.1", "exports": { ".": "./mod.ts", "./mod": "./mod.ts" diff --git a/src/ControllerMethodArgs.ts b/src/ControllerMethodArgs.ts index c1a33af..8276c78 100644 --- a/src/ControllerMethodArgs.ts +++ b/src/ControllerMethodArgs.ts @@ -129,14 +129,16 @@ function getEnhancedHandler( parsedReqBody = await _internal.parseOakReqBody(ctx); } catch (e) { if ( - ctx.request.method === "GET" && + ["GET", "DELETE", "HEAD"].includes(ctx.request.method) && ctx.request.headers.get("Content-Type") === "application/json" && (e as Error).message?.includes("Unexpected end of JSON input") ) { // we ignore this parsing error because the client was sending - // a weird combination of method & content-type header + // a weird combination of method & content-type header, but here to + // the "Japanese engineering mindset": + // https://www.500eboard.co/forums/threads/engineers-japanese-vs-german.14695/ } else { - // for other case, we trigger the error back to userland + // for other scenarios, we trigger the error back to userland return ctx.throw( 400, `Unable to parse request body: ${(e as Error).message}`, diff --git a/src/ControllerMethodArgs_test.ts b/src/ControllerMethodArgs_test.ts index 0278205..f550b88 100644 --- a/src/ControllerMethodArgs_test.ts +++ b/src/ControllerMethodArgs_test.ts @@ -636,30 +636,47 @@ Deno.test("getEnhancedHandler with a faulty ctx.request.body", async () => { spyParseOakRequestBody.restore(); }); -Deno.test("getEnhancedHandler with a faulty request method and content-type combination", async () => { +Deno.test("getEnhancedHandler with a non-conventional request method and content-type combination", async (t) => { + const methodsToTest = ["GET", "HEAD", "DELETE"]; const spyParseOakRequestBody = spy(_internal, "parseOakReqBody"); - function testHandler() { - return "weird.method.content-type.combination.handled"; - } - // deno-lint-ignore ban-types - const enhancedHandler: Function = _internal.getEnhancedHandler(testHandler); - const ctx = createMockContext({ - method: "GET", - headers: [["Content-Type", "application/json"]], - }); - Object.defineProperty(ctx.request, "body", { - get: () => createMockRequestBody("json", "Unexpected end of JSON input"), - }); - const spyCtxThrow = spy(); - Object.defineProperty(ctx, "throw", { - value: (errorStatus: unknown, message?: string, props?: unknown) => { - spyCtxThrow(errorStatus, message, props); - }, - }); - const retVal = await enhancedHandler(ctx); - assertSpyCalls(spyCtxThrow, 0); - assertSpyCalls(spyParseOakRequestBody, 1); - assertEquals(retVal, "weird.method.content-type.combination.handled"); + await Promise.all(methodsToTest.map((method) => + t.step({ + name: `testing content-type: application/json and ${method} request`, + fn: async () => { + function testHandler() { + return `method ${method} and Content-Type: application/json handled`; + } + // deno-lint-ignore ban-types + const enhancedHandler: Function = _internal.getEnhancedHandler( + testHandler, + ); + const ctx = createMockContext({ + method, // GET | HEAD | DELETE + headers: [["Content-Type", "application/json"]], + }); + Object.defineProperty(ctx.request, "body", { + get: () => + createMockRequestBody("json", "Unexpected end of JSON input"), + }); + const spyCtxThrow = spy(); + Object.defineProperty(ctx, "throw", { + value: (errorStatus: unknown, message?: string, props?: unknown) => { + spyCtxThrow(errorStatus, message, props); + }, + }); + const retVal = await enhancedHandler(ctx); + assertSpyCalls(spyCtxThrow, 0); + assertEquals( + retVal, + `method ${method} and Content-Type: application/json handled`, + ); + }, + sanitizeOps: false, + sanitizeResources: false, + sanitizeExit: false, + }) + )); + assertSpyCalls(spyParseOakRequestBody, methodsToTest.length); spyParseOakRequestBody.restore(); });