Skip to content

Commit 0eda10b

Browse files
fix: Return MiddlewareResponse obj for rewrite (#2159)
* fix: Return MiddlewareResponse for rewrite * chore: remove console logs * chore: added tests and updated logic to include init * chore: revert selector back to original * fix: pass in new headers * chore: applyheader before response * chore: update tests remove header fix * chore: testing update * chore: removed undici import * chore: added undici import back * Update demos/middleware/middleware.ts Co-authored-by: Nick Taylor <[email protected]> * Update cypress/e2e/middleware/enhanced.cy.ts update pageProps testing Co-authored-by: Nick Taylor <[email protected]> * chore: removed header override testing * chore: formatting * chore: new line --------- Co-authored-by: Nick Taylor <[email protected]>
1 parent f5d6d6f commit 0eda10b

File tree

7 files changed

+92
-13
lines changed

7 files changed

+92
-13
lines changed

cypress/e2e/middleware/enhanced.cy.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
describe('Enhanced middleware', () => {
2-
it('rewrites the response body', () => {
2+
it('rewrites the response body using request.rewrite()', () => {
3+
cy.visit('/request-rewrite')
4+
cy.get('#message').contains('This was static (& escaping test &amp;) but has been transformed in')
5+
cy.contains("This is an ad that isn't shown by default")
6+
})
7+
8+
it('modifies the page props when using request.rewrite()', () => {
9+
cy.visit('/request-rewrite')
10+
cy.get('script#__NEXT_DATA__').then((element) => {
11+
const { props } = JSON.parse(element.text());
12+
expect(props.pageProps.message).to.include('This was static (& escaping test &amp;) but has been transformed in')
13+
})
14+
})
15+
16+
it('rewrites the response body using request.next()', () => {
317
cy.visit('/static')
418
cy.get('#message').contains('This was static (& escaping test &amp;) but has been transformed in')
519
cy.contains("This is an ad that isn't shown by default")

demos/middleware/middleware.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,22 @@ export async function middleware(req: NextRequest) {
2727
const message = `This was static (& escaping test &amp;) but has been transformed in ${req.geo?.city}`
2828

2929
// Transform the response HTML and props
30-
res.replaceText('p[id=message]', message)
30+
res.replaceText('#message', message)
31+
res.setPageProp('message', message)
32+
res.setPageProp('showAd', true)
33+
34+
res.headers.set('x-modified-edge', 'true')
35+
res.headers.set('x-is-deno', 'Deno' in globalThis ? 'true' : 'false')
36+
return res
37+
}
38+
39+
if (pathname.startsWith('/request-rewrite')) {
40+
// request.rewrite() should return the MiddlewareResponse object instead of the Response object.
41+
const res = await request.rewrite('/static-rewrite')
42+
const message = `This was static (& escaping test &amp;) but has been transformed in ${req.geo?.city}`
43+
44+
// Transform the response HTML and props
45+
res.replaceText('#message', message)
3146
res.setPageProp('message', message)
3247
res.setPageProp('showAd', true)
3348

@@ -138,6 +153,7 @@ export const config = {
138153
'/headers',
139154
'/cookies/:path*',
140155
{ source: '/static' },
156+
{source: '/request-rewrite' },
141157
{ source: '/matcher-cookie'},
142158
{ source: '/shows/((?!99|88).*)' },
143159
{
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const Rewrite = () => {
2+
return (
3+
<div>
4+
<p>This should have been rewritten</p>
5+
</div>
6+
)
7+
}
8+
9+
export default Rewrite
10+
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as React from 'react'
2+
3+
const useHydrated = () => {
4+
const [hydrated, setHydrated] = React.useState(false)
5+
React.useEffect(() => {
6+
setHydrated(true)
7+
}, [])
8+
return hydrated
9+
}
10+
11+
const Page = ({ message, showAd }) => {
12+
const hydrated = useHydrated()
13+
return (
14+
<div>
15+
<p id="message">{message}</p>
16+
{hydrated && showAd ? (
17+
<div>
18+
<p>This is an ad that isn't shown by default on static test 2 page</p>
19+
<img src="http://placekitten.com/400/300" />
20+
</div>
21+
) : (
22+
<p>No ads for me</p>
23+
)}
24+
</div>
25+
)
26+
}
27+
28+
export async function getStaticProps() {
29+
return {
30+
props: {
31+
message: 'This is a static page',
32+
showAd: false,
33+
},
34+
}
35+
}
36+
37+
export default Page

packages/next/src/middleware/request.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Context } from '@netlify/edge-functions'
22
import type { NextURL } from 'next/dist/server/web/next-url'
3-
import { NextResponse } from 'next/server'
43
import type { NextRequest as InternalNextRequest } from 'next/server'
54

65
import { MiddlewareResponse } from './response'
@@ -64,16 +63,17 @@ export class MiddlewareRequest extends Request {
6463
if (response.status === 301 && locationHeader?.startsWith('/')) {
6564
response = await this.context.rewrite(locationHeader)
6665
}
67-
6866
return new MiddlewareResponse(response)
6967
}
7068

71-
rewrite(destination: string | URL | NextURL, init?: ResponseInit): NextResponse {
69+
async rewrite(destination: string | URL | NextURL, init?: ResponseInit) {
7270
if (typeof destination === 'string' && destination.startsWith('/')) {
7371
destination = new URL(destination, this.url)
7472
}
7573
this.applyHeaders()
76-
return NextResponse.rewrite(destination, init)
74+
const response = await this.context.rewrite(destination)
75+
76+
return new MiddlewareResponse(response, init)
7777
}
7878

7979
get headers() {

packages/next/src/middleware/response.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ export type NextDataTransform = <T extends { pageProps?: Record<string, any> }>(
1010
export class MiddlewareResponse extends NextResponse {
1111
private readonly dataTransforms: NextDataTransform[]
1212
private readonly elementHandlers: Array<[selector: string, handlers: ElementHandlers]>
13-
constructor(public originResponse: Response) {
13+
14+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
15+
constructor(public originResponse: Response, init?: ResponseInit) {
1416
// we need to propagate the set-cookie header, so response.cookies.get works correctly
1517
const initHeaders = new Headers()
1618
if (originResponse.headers.has('set-cookie')) {

test/e2e/next-test-lib/next-test-utils.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import spawn from 'cross-spawn'
33
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs'
44
import { writeFile } from 'fs-extra'
5-
import { fetch as undiciFetch } from 'undici'
5+
import { fetch as undiciFetch } from 'next/dist/compiled/undici'
66
import nodeFetch from 'node-fetch'
77
import path from 'path'
88
import qs from 'querystring'
@@ -111,7 +111,7 @@ async function processChunkedResponse(response) {
111111
* @param {string | number} appPort
112112
* @param {string} pathname
113113
* @param {Record<string, any> | string | undefined} [query]
114-
* @param {import("undici").RequestInit} [opts]
114+
* @param {import("next/dist/compiled/undici").RequestInit} [opts]
115115
* @returns {Promise<string>}
116116
*/
117117
export function renderViaHTTP(appPort, pathname, query, opts) {
@@ -121,11 +121,11 @@ export function renderViaHTTP(appPort, pathname, query, opts) {
121121
/**
122122
* @param {string | number} appPort
123123
* @param {string} pathname
124-
* @param {Record<string, any> | string | undefined} query
125-
* @param {RequestInit} opts
126-
* @returns {Promise<Response>}
124+
* @param {Record<string, any> | string | null | undefined} [query]
125+
* @param {import('node-fetch').RequestInit} [opts]
126+
* @returns {Promise<Response & {buffer: any} & {headers: Headers}>}
127127
*/
128-
export async function fetchViaHTTP(appPort, pathname, query = undefined, opts = undefined, useUndici = false) {
128+
export function fetchViaHTTP(appPort, pathname, query, opts, useUndici = false) {
129129
const url = `${pathname}${typeof query === 'string' ? query : query ? `?${qs.stringify(query)}` : ''}`
130130
const fetch = useUndici ? undiciFetch : nodeFetch
131131
const fullUrl = getFullUrl(appPort, url)

0 commit comments

Comments
 (0)