From 1c4f67bdceb48aca5ba45be7a9bef28eebbc8bc7 Mon Sep 17 00:00:00 2001 From: Steven S Date: Tue, 12 Mar 2024 23:10:47 +0000 Subject: [PATCH] fix: 216 withContent returning 500 (#217) Fix to return a 400 instead of a 500 when using the `withContent` middleware and sending no content of invalid JSON in the request body. --- src/withContent.spec.ts | 60 +++++++++++++++++++++++++++++++++++++++++ src/withContent.ts | 10 +++++-- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/withContent.spec.ts b/src/withContent.spec.ts index cad8d6d5..8d520e6c 100644 --- a/src/withContent.spec.ts +++ b/src/withContent.spec.ts @@ -2,6 +2,7 @@ import 'isomorphic-fetch' import { describe, expect, it, vi } from 'vitest' import { Router } from './Router' import { withContent } from './withContent' +import { StatusError } from './StatusError' describe('withContent (middleware)', () => { it('can access the awaited Response body as request.content', async () => { @@ -19,4 +20,63 @@ describe('withContent (middleware)', () => { expect(handler).toHaveReturnedWith({ foo: 'bar' }) }) + + it('throws an "Unexpected end of JSON input" error when no content is sent in the body', async () => { + const router = Router() + const handler = vi.fn(({ content }) => content) + const request = new Request('https://foo.bar', { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }) + + await expect( + router.post('/', withContent, handler).handle(request) + ).rejects.toThrowError(/Unexpected end of JSON input/) + + expect(handler).not.toHaveBeenCalled() + expect(handler).not.toHaveReturned() + }) + + it('returns a 400 when no content is sent in the body', async () => { + const router = Router() + const handler = vi.fn(({ content }) => content) + const request = new Request('https://foo.bar', { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + }) + + try { + await router.post('/', withContent, handler).handle(request) + } catch (e) { + expect(e).toBeInstanceOf(StatusError) + expect(e).toContain({ status: 400 }) + } + expect(handler).not.toHaveBeenCalled() + expect(handler).not.toHaveReturned() + }) + + it('returns a 400 when invalid JSON content is sent in the body', async () => { + const router = Router() + const handler = vi.fn(({ content }) => content) + const request = new Request('https://foo.bar', { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: 'foo is invalid JSON', + }) + + try { + await router.post('/', withContent, handler).handle(request) + } catch (e) { + expect(e).toBeInstanceOf(StatusError) + expect(e).toContain({ status: 400 }) + } + expect(handler).not.toHaveBeenCalled() + expect(handler).not.toHaveReturned() + }) }) diff --git a/src/withContent.ts b/src/withContent.ts index 06c93d3c..79fa3c77 100644 --- a/src/withContent.ts +++ b/src/withContent.ts @@ -1,11 +1,17 @@ import { IRequest, IRequestStrict } from './Router' +import { StatusError } from './StatusError' export type HasContent = { - content: ContentType, + content: ContentType } & IRequestStrict // withContent - embeds any request body as request.content export const withContent = async (request: IRequest): Promise => { if (request.headers.get('content-type')?.includes('json')) - request.content = await request.json() + try { + request.content = await request.json() + } catch (e: unknown) { + const se = e as SyntaxError + throw new StatusError(400, se.message) + } }