Skip to content

Commit

Permalink
Add res.redirect response helper (vercel#14705)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
  • Loading branch information
2 people authored and rokinsky committed Jul 11, 2020
1 parent 8e186f2 commit dc8dbe0
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 2 deletions.
3 changes: 1 addition & 2 deletions docs/advanced-features/preview-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ export default async (req, res) => {

// Redirect to the path from the fetched post
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
res.writeHead(307, { Location: post.slug })
res.end()
res.redirect(post.slug)
}
```

Expand Down
1 change: 1 addition & 0 deletions docs/api-routes/response-helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ The included helpers are:
- `res.status(code)` - A function to set the status code. `code` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
- `res.json(json)` - Sends a JSON response. `json` must be a valid JSON object
- `res.send(body)` - Sends the HTTP response. `body` can be a `string`, an `object` or a `Buffer`
- `res.redirect([status,] path)` - Redirects to a specified path or URL. `status` must be a valid [HTTP status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). If not specified, `status` defaults to "302" "Found".
1 change: 1 addition & 0 deletions packages/next/next-server/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export type NextApiResponse<T = any> = ServerResponse & {
*/
json: Send<T>
status: (statusCode: number) => NextApiResponse<T>
redirect: (statusOrUrl: string | number, url?: string) => NextApiResponse<T>

/**
* Set preview data for Next.js' prerender mode
Expand Down
21 changes: 21 additions & 0 deletions packages/next/next-server/server/api-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,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.redirect = (statusOrUrl, url) => redirect(apiRes, statusOrUrl, url)
apiRes.setPreviewData = (data, options = {}) =>
setPreviewData(apiRes, data, Object.assign({}, apiContext, options))
apiRes.clearPreviewData = () => clearPreviewData(apiRes)
Expand Down Expand Up @@ -218,6 +219,26 @@ export function sendStatusCode(
return res
}

/**
*
* @param res response object
* @param [statusOrUrl] `HTTP` status code of redirect
* @param url URL of redirect
*/
export function redirect(
res: NextApiResponse,
statusOrUrl: string | number,
url?: string
): NextApiResponse<any> {
if (typeof statusOrUrl === 'string') {
url = statusOrUrl
statusOrUrl = 307
}

res.writeHead(statusOrUrl, { Location: url }).end()
return res
}

function sendEtagResponse(
req: NextApiRequest,
res: NextApiResponse,
Expand Down
3 changes: 3 additions & 0 deletions test/integration/api-support/pages/api/redirect-301.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default (req, res) => {
res.redirect(301, '/login')
}
3 changes: 3 additions & 0 deletions test/integration/api-support/pages/api/redirect-307.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default (req, res) => {
res.redirect('/login')
}
23 changes: 23 additions & 0 deletions test/integration/api-support/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,29 @@ function runTests(dev = false) {
expect(data).toEqual({ message: 'Parsed body' })
})

it('should redirect with status code 307', async () => {
const res = await fetchViaHTTP(appPort, '/api/redirect-307', null, {
redirect: 'manual',
})

expect(res.status).toEqual(307)
})

it('should redirect to login', async () => {
const res = await fetchViaHTTP(appPort, '/api/redirect-307', null, {})

expect(res.redirected).toBe(true)
expect(res.url).toContain('/login')
})

it('should redirect with status code 301', async () => {
const res = await fetchViaHTTP(appPort, '/api/redirect-301', null, {
redirect: 'manual',
})

expect(res.status).toEqual(301)
})

it('should return empty query object', async () => {
const data = await fetchViaHTTP(appPort, '/api/query', null, {}).then(
(res) => res.ok && res.json()
Expand Down

0 comments on commit dc8dbe0

Please sign in to comment.