b78c28f7a0
This PR introduces a more predictable API to manipulate cookies in an Edge Function context. ```js const response = new NextResponse() // set a cookie response.cookies.set('foo, 'bar') // => set-cookie: 'foo=bar; Path=/'` // set another cookie response.cookies.set('fooz, 'barz') // => set-cookie: 'foo=bar; Path=/, fooz=barz; Path=/'` // delete a cookie means mark it as expired response.cookies.delete('foo') // => set-cookie: 'foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT, fooz=barz; Path=/'` // clear all cookies means mark all of them as expired response.cookies.clear() // => set-cookie: 'fooz=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT, foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT'` ``` This new cookies API uses [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) interface, and it's available for `NextRequest` and `NextResponse`. Additionally, you can pass a specific cookies option as a third argument in `set` method: ```js response.cookies.set('foo', 'bar', { path: '/', maxAge: 60 * 60 * 24 * 7, httpOnly: true, sameSite: 'strict', domain: 'example.com' } ``` **Note**: `maxAge` it's in seconds rather than milliseconds. Any cookie manipulation will be reflected over the `set-cookie` header, transparently. closes #31719
85 lines
2 KiB
TypeScript
85 lines
2 KiB
TypeScript
import type { I18NConfig } from '../../config-shared'
|
|
import { NextURL } from '../next-url'
|
|
import { toNodeHeaders, validateURL } from '../utils'
|
|
|
|
import { NextCookies } from './cookies'
|
|
|
|
const INTERNALS = Symbol('internal response')
|
|
const REDIRECTS = new Set([301, 302, 303, 307, 308])
|
|
|
|
export class NextResponse extends Response {
|
|
[INTERNALS]: {
|
|
cookies: NextCookies
|
|
url?: NextURL
|
|
}
|
|
|
|
constructor(body?: BodyInit | null, init: ResponseInit = {}) {
|
|
super(body, init)
|
|
|
|
this[INTERNALS] = {
|
|
cookies: new NextCookies(this),
|
|
url: init.url
|
|
? new NextURL(init.url, {
|
|
basePath: init.nextConfig?.basePath,
|
|
i18n: init.nextConfig?.i18n,
|
|
trailingSlash: init.nextConfig?.trailingSlash,
|
|
headers: toNodeHeaders(this.headers),
|
|
})
|
|
: undefined,
|
|
}
|
|
}
|
|
|
|
public get cookies() {
|
|
return this[INTERNALS].cookies
|
|
}
|
|
|
|
static json(body: any, init?: ResponseInit) {
|
|
const { headers, ...responseInit } = init || {}
|
|
return new NextResponse(JSON.stringify(body), {
|
|
...responseInit,
|
|
headers: {
|
|
...headers,
|
|
'content-type': 'application/json',
|
|
},
|
|
})
|
|
}
|
|
|
|
static redirect(url: string | NextURL | URL, status = 307) {
|
|
if (!REDIRECTS.has(status)) {
|
|
throw new RangeError(
|
|
'Failed to execute "redirect" on "response": Invalid status code'
|
|
)
|
|
}
|
|
|
|
const destination = validateURL(url)
|
|
return new NextResponse(destination, {
|
|
headers: { Location: destination },
|
|
status,
|
|
})
|
|
}
|
|
|
|
static rewrite(destination: string | NextURL | URL) {
|
|
return new NextResponse(null, {
|
|
headers: {
|
|
'x-middleware-rewrite': validateURL(destination),
|
|
},
|
|
})
|
|
}
|
|
|
|
static next() {
|
|
return new NextResponse(null, {
|
|
headers: {
|
|
'x-middleware-next': '1',
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
interface ResponseInit extends globalThis.ResponseInit {
|
|
nextConfig?: {
|
|
basePath?: string
|
|
i18n?: I18NConfig
|
|
trailingSlash?: boolean
|
|
}
|
|
url?: string
|
|
}
|