From ead10f1f8113f24f3d6dc4e387204da75248b72e Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 25 Aug 2021 10:52:43 -0500 Subject: [PATCH] Fix handling for 204 status code with a body (#28479) This ensures we handle 204 and 304 status codes correctly in API routes. ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have helpful link attached, see `contributing.md` Fixes: https://github.com/vercel/next.js/issues/28464 --- errors/invalid-api-status-body.md | 32 +++++++++++++++++++ errors/manifest.json | 4 +++ packages/next/server/api-utils.ts | 16 ++++++++++ .../api-support/pages/api/status-204.js | 7 ++++ .../api-support/test/index.test.js | 30 +++++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 errors/invalid-api-status-body.md create mode 100644 test/integration/api-support/pages/api/status-204.js diff --git a/errors/invalid-api-status-body.md b/errors/invalid-api-status-body.md new file mode 100644 index 000000000000000..b0d5fd60fd99d52 --- /dev/null +++ b/errors/invalid-api-status-body.md @@ -0,0 +1,32 @@ +Invalid API Route Status/Body Response + +#### Why This Error Occurred + +In one of your API routes a 204 or 304 status code was used as well as sending a response body. + +This is invalid as a 204 or 304 status code dictates no response body should be present. + +#### Possible Ways to Fix It + +Send an empty body when using a 204 or 304 status code or use a different status code while sending a response body. + +Before + +```js +export default function handler(req, res) { + res.status(204).send('invalid body') +} +``` + +After + +```js +export default function handler(req, res) { + res.status(204).send() +} +``` + +### Useful Links + +- [204 status code documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204) +- [304 status code documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304) diff --git a/errors/manifest.json b/errors/manifest.json index d56d23abdcad5b9..f1580c3b658fd07 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -454,6 +454,10 @@ { "title": "next-config-error", "path": "/errors/next-config-error.md" + }, + { + "title": "invalid-api-status-body", + "path": "/errors/invalid-api-status-body.md" } ] } diff --git a/packages/next/server/api-utils.ts b/packages/next/server/api-utils.ts index 0ee3b47bffbaad5..f6ef0654012d854 100644 --- a/packages/next/server/api-utils.ts +++ b/packages/next/server/api-utils.ts @@ -258,6 +258,22 @@ export function sendData( return } + // strip irrelevant headers/body + if (res.statusCode === 204 || res.statusCode === 304) { + res.removeHeader('Content-Type') + res.removeHeader('Content-Length') + res.removeHeader('Transfer-Encoding') + + if (process.env.NODE_ENV === 'development' && body) { + console.warn( + `A body was attempted to be set with a 204 statusCode for ${req.url}, this is invalid and the body was ignored.\n` + + `See more info here https://nextjs.org/docs/messages/invalid-api-status-body` + ) + } + res.end() + return + } + const contentType = res.getHeader('Content-Type') if (body instanceof Stream) { diff --git a/test/integration/api-support/pages/api/status-204.js b/test/integration/api-support/pages/api/status-204.js new file mode 100644 index 000000000000000..659ca8b5099a6f2 --- /dev/null +++ b/test/integration/api-support/pages/api/status-204.js @@ -0,0 +1,7 @@ +export default function handler(req, res) { + if (req.query.invalid) { + // test the warning when content is added for a 204 response + return res.status(204).json({ hello: 'world' }) + } + return res.status(204).send() +} diff --git a/test/integration/api-support/test/index.test.js b/test/integration/api-support/test/index.test.js index 05a960be3a2f7f7..e5c95cea4690356 100644 --- a/test/integration/api-support/test/index.test.js +++ b/test/integration/api-support/test/index.test.js @@ -26,6 +26,36 @@ let mode let app function runTests(dev = false) { + it('should handle 204 status correctly', async () => { + const res = await fetchViaHTTP(appPort, '/api/status-204', undefined, { + redirect: 'manual', + }) + expect(res.status).toBe(204) + expect(res.headers.get('content-type')).toBe(null) + expect(res.headers.get('content-length')).toBe(null) + expect(res.headers.get('transfer-encoding')).toBe(null) + + const stderrIdx = stderr.length + const res2 = await fetchViaHTTP( + appPort, + '/api/status-204', + { invalid: '1' }, + { + redirect: 'manual', + } + ) + expect(res2.status).toBe(204) + expect(res2.headers.get('content-type')).toBe(null) + expect(res2.headers.get('content-length')).toBe(null) + expect(res2.headers.get('transfer-encoding')).toBe(null) + + if (dev) { + expect(stderr.substr(stderrIdx)).toContain( + 'A body was attempted to be set with a 204 statusCode' + ) + } + }) + it('should render page', async () => { const html = await renderViaHTTP(appPort, '/') expect(html).toMatch(/API - support/)