From 94a28a664dc65f7bbc6f848268aced9a9a503ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 24 Jan 2022 02:35:49 +0100 Subject: [PATCH 1/2] fix: allow string and `null` in `res.json()` API route method --- packages/next/server/api-utils.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/packages/next/server/api-utils.ts b/packages/next/server/api-utils.ts index a8bc956f4f6bfc3..b725cc56a940e51 100644 --- a/packages/next/server/api-utils.ts +++ b/packages/next/server/api-utils.ts @@ -89,7 +89,7 @@ export async function apiResolver( } apiRes.status = (statusCode) => sendStatusCode(apiRes, statusCode) apiRes.send = (data) => sendData(apiReq, apiRes, data) - apiRes.json = (data) => sendJson(apiRes, data) + apiRes.json = apiRes.send apiRes.redirect = (statusOrUrl: number | string, url?: string) => redirect(apiRes, statusOrUrl, url) apiRes.setPreviewData = (data, options = {}) => @@ -266,7 +266,7 @@ export function sendData( res: NextApiResponse, body: any ): void { - if (body === null || body === undefined) { + if (body === undefined) { res.end() return } @@ -297,7 +297,10 @@ export function sendData( return } - const isJSONLike = ['object', 'number', 'boolean'].includes(typeof body) + const isJSONLike = ['object', 'number', 'boolean', 'string'].includes( + typeof body + ) + const stringifiedBody = isJSONLike ? JSON.stringify(body) : body const etag = generateETag(stringifiedBody) if (sendEtagResponse(req, res, etag)) { @@ -321,19 +324,6 @@ export function sendData( res.end(stringifiedBody) } -/** - * Send `JSON` object - * @param res response object - * @param jsonBody of data - */ -export function sendJson(res: NextApiResponse, jsonBody: any): void { - // Set header to application/json - res.setHeader('Content-Type', 'application/json; charset=utf-8') - - // Use send to handle request - res.send(jsonBody) -} - const COOKIE_NAME_PRERENDER_BYPASS = `__prerender_bypass` const COOKIE_NAME_PRERENDER_DATA = `__next_preview_data` From ef762273d0497558823ea0730d1a61fbdb0a0452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 24 Jan 2022 02:38:01 +0100 Subject: [PATCH 2/2] test: ensure `json` API route/Middleware consistency --- .../api-support/pages/api/json-null.js | 3 ++ .../api-support/pages/api/json-string.js | 3 ++ .../api/{undefined.js => json-undefined.js} | 0 .../api-support/test/index.test.js | 28 ++++++++++++++++--- 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 test/integration/api-support/pages/api/json-null.js create mode 100644 test/integration/api-support/pages/api/json-string.js rename test/integration/api-support/pages/api/{undefined.js => json-undefined.js} (100%) diff --git a/test/integration/api-support/pages/api/json-null.js b/test/integration/api-support/pages/api/json-null.js new file mode 100644 index 000000000000000..b0bac6f5f9fad52 --- /dev/null +++ b/test/integration/api-support/pages/api/json-null.js @@ -0,0 +1,3 @@ +export default (req, res) => { + res.json(null) +} diff --git a/test/integration/api-support/pages/api/json-string.js b/test/integration/api-support/pages/api/json-string.js new file mode 100644 index 000000000000000..f8eb8f057157354 --- /dev/null +++ b/test/integration/api-support/pages/api/json-string.js @@ -0,0 +1,3 @@ +export default (req, res) => { + res.json('Hello world!') +} diff --git a/test/integration/api-support/pages/api/undefined.js b/test/integration/api-support/pages/api/json-undefined.js similarity index 100% rename from test/integration/api-support/pages/api/undefined.js rename to test/integration/api-support/pages/api/json-undefined.js diff --git a/test/integration/api-support/test/index.test.js b/test/integration/api-support/test/index.test.js index 6468b48dd36bcf4..c2da66bebb9a7e7 100644 --- a/test/integration/api-support/test/index.test.js +++ b/test/integration/api-support/test/index.test.js @@ -94,7 +94,7 @@ function runTests(dev = false) { const text = await fetchViaHTTP(appPort, '/api', null, {}).then( (res) => res.ok && res.text() ) - expect(text).toEqual('Index should work') + expect(text).toEqual('"Index should work"') }) it('should return custom error', async () => { @@ -162,8 +162,28 @@ function runTests(dev = false) { expect(body).toBe(true) }) + it('should support string for JSON in api page', async () => { + const res = await fetchViaHTTP(appPort, '/api/json-string') + const body = res.ok ? await res.json() : 'Some error' + expect(res.status).toBe(200) + expect(res.headers.get('content-type')).toBe( + 'application/json; charset=utf-8' + ) + expect(body).toBe('Hello world!') + }) + + it('should support null for JSON in api page', async () => { + const res = await fetchViaHTTP(appPort, '/api/json-null') + const body = res.ok ? await res.json() : 'Not null' + expect(res.status).toBe(200) + expect(res.headers.get('content-type')).toBe( + 'application/json; charset=utf-8' + ) + expect(body).toBe(null) + }) + it('should support undefined response body', async () => { - const res = await fetchViaHTTP(appPort, '/api/undefined', null, {}) + const res = await fetchViaHTTP(appPort, '/api/json-undefined', null, {}) const body = res.ok ? await res.text() : null expect(body).toBe('') }) @@ -501,7 +521,7 @@ function runTests(dev = false) { expect(stderr).toContain( `API resolved without sending a response for ${apiURL}, this may result in stalled requests.` ) - expect(await req.text()).toBe('hello world') + expect(await req.text()).toBe('"hello world"') }) it('should not show warning if using externalResolver flag', async () => { @@ -511,7 +531,7 @@ function runTests(dev = false) { expect(stderr.substr(startIdx)).not.toContain( `API resolved without sending a response for ${apiURL}` ) - expect(await req.text()).toBe('hello world') + expect(await req.text()).toBe('"hello world"') }) } else { it('should show warning with next export', async () => {