Skip to content

Commit a630ab4

Browse files
Skn0tttaty2010
andauthored
fix: support setting cookies from MiddlewareResponse (#2027)
* fix: try building repro * fix: install next middleware before building * fix: update test expectation * fix: use local version of utils * fix: typo * fix: some more * fix: the bug * fix: typo * fix: revert changes to next.config.js * fix: typo * fix: oops * fix: header exists (see .has above) * fix: use headers.get * fix: move test into cypress code * fix: revert changes to e2e * fix: remove console.log * fix: add another assertion for headers * revert one more line * chore: trigger build * chore: updated config to include cookies/middleware --------- Co-authored-by: Tatyana <[email protected]> Co-authored-by: taty2010 <[email protected]>
1 parent 7c1673b commit a630ab4

File tree

5 files changed

+45
-2
lines changed

5 files changed

+45
-2
lines changed

cypress/integration/middleware/standard.spec.ts

+8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ describe('Standard middleware', () => {
3737
cy.getCookie('netlifyCookie').should('have.property', 'value', 'true')
3838
})
3939
})
40+
41+
// https://github.com/netlify/pillar-support/issues/350
42+
it('MiddlewareResponse adds cookies', () => {
43+
cy.request('/cookies/middleware').then((response) => {
44+
cy.getCookie('middlewareCookie').should('have.property', 'value', 'true')
45+
expect(response.headers).to.have.property('x-foo', 'bar')
46+
})
47+
})
4048
})
4149

4250
describe('Middleware matchers', () => {

demos/middleware/middleware.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ export async function middleware(req: NextRequest) {
5353
return request.rewrite('/api/hello')
5454
}
5555

56+
if (pathname.startsWith('/cookies/middleware')) {
57+
const response = await new MiddlewareRequest(req).next()
58+
response.cookies.set('middlewareCookie', 'true')
59+
response.headers.set('x-foo', 'bar')
60+
return response
61+
}
62+
5663
if (pathname.startsWith('/cookies')) {
5764
response = NextResponse.next()
5865
response.cookies.set('netlifyCookie', 'true')
@@ -129,8 +136,8 @@ export const config = {
129136
matcher: [
130137
'/api/:all*',
131138
'/headers',
139+
'/cookies/:path*',
132140
{ source: '/static' },
133-
{ source: '/cookies' },
134141
{ source: '/matcher-cookie'},
135142
{ source: '/shows/((?!99|88).*)' },
136143
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const Cookies = () => {
2+
return (
3+
<div>
4+
<p>The cookie "middlewareCookie" should be set to true</p>
5+
</div>
6+
)
7+
}
8+
9+
export default Cookies

packages/next/src/middleware/response.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ export class MiddlewareResponse extends NextResponse {
1111
private readonly dataTransforms: NextDataTransform[]
1212
private readonly elementHandlers: Array<[selector: string, handlers: ElementHandlers]>
1313
constructor(public originResponse: Response) {
14-
super()
14+
// we need to propagate the set-cookie header, so response.cookies.get works correctly
15+
const initHeaders = new Headers()
16+
if (originResponse.headers.has('set-cookie')) {
17+
initHeaders.set('set-cookie', originResponse.headers.get('set-cookie'))
18+
}
19+
20+
super(undefined, {
21+
headers: initHeaders,
22+
})
1523

1624
// These are private in Node when compiling, but we access them in Deno at runtime
1725
Object.defineProperty(this, 'dataTransforms', {

packages/runtime/src/templates/edge-shared/utils.ts

+11
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,15 @@ export const addMiddlewareHeaders = async (
5757
return response
5858
}
5959

60+
interface ResponseCookies {
61+
readonly _headers: Headers
62+
}
63+
6064
interface MiddlewareResponse extends Response {
6165
originResponse: Response
6266
dataTransforms: NextDataTransform[]
6367
elementHandlers: Array<[selector: string, handlers: ElementHandlers]>
68+
get cookies(): ResponseCookies
6469
}
6570

6671
interface MiddlewareRequest {
@@ -184,6 +189,12 @@ export const buildResponse = async ({
184189
if (request.method === 'HEAD' || request.method === 'OPTIONS') {
185190
return response.originResponse
186191
}
192+
193+
// NextResponse doesn't set cookies onto the originResponse, so we need to copy them over
194+
if (response.cookies._headers.has('set-cookie')) {
195+
response.originResponse.headers.set('set-cookie', response.cookies._headers.get('set-cookie')!)
196+
}
197+
187198
// If it's JSON we don't need to use the rewriter, we can just parse it
188199
if (response.originResponse.headers.get('content-type')?.includes('application/json')) {
189200
const props = await response.originResponse.json()

0 commit comments

Comments
 (0)