Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable allowMiddlewareResponseBody by default #44224

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 1 addition & 12 deletions docs/advanced-features/middleware.md
Expand Up @@ -220,18 +220,7 @@ export function middleware(request: NextRequest) {

## Producing a Response

You can respond to middleware directly by returning a `NextResponse` (responding from middleware is available since Next.js v13.0.0).

To enable middleware responses, update `next.config.js`:

```js
// next.config.js
module.exports = {
experimental: {
allowMiddlewareResponseBody: true,
},
}
```
You can respond to middleware directly by returning a `NextResponse` (responding from middleware is available since Next.js v13.1.0).

Once enabled, you can provide a response from middleware using the `Response` or `NextResponse` API:

Expand Down
8 changes: 0 additions & 8 deletions packages/next/build/webpack-config.ts
Expand Up @@ -301,9 +301,6 @@ export function getDefineEnv({
'process.env.__NEXT_I18N_SUPPORT': JSON.stringify(!!config.i18n),
'process.env.__NEXT_I18N_DOMAINS': JSON.stringify(config.i18n?.domains),
'process.env.__NEXT_ANALYTICS_ID': JSON.stringify(config.analyticsId),
'process.env.__NEXT_ALLOW_MIDDLEWARE_RESPONSE_BODY': JSON.stringify(
config.allowMiddlewareResponseBody
),
'process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE': JSON.stringify(
config.skipMiddlewareUrlNormalize
),
Expand Down Expand Up @@ -2062,7 +2059,6 @@ export default async function getBaseWebpackConfig(
dev,
sriEnabled: !dev && !!config.experimental.sri?.algorithm,
hasFontLoaders: !!config.experimental.fontLoaders,
allowMiddlewareResponseBody: !!config.allowMiddlewareResponseBody,
}),
isClient &&
new BuildManifestPlugin({
Expand Down Expand Up @@ -2148,10 +2144,6 @@ export default async function getBaseWebpackConfig(
['swcEmotion', !!config.compiler?.emotion],
['turbotrace', !!config.experimental.turbotrace],
['transpilePackages', !!config.transpilePackages],
[
'allowMiddlewareResponseBody',
!!config.allowMiddlewareResponseBody,
],
[
'skipMiddlewareUrlNormalize',
!!config.skipMiddlewareUrlNormalize,
Expand Down
Expand Up @@ -40,7 +40,7 @@ export default function middlewareLoader(this: any) {
buildInfo.rootDir = rootDir

return `
import { adapter, blockUnallowedResponse, enhanceGlobals } from 'next/dist/esm/server/web/adapter'
import { adapter, enhanceGlobals } from 'next/dist/esm/server/web/adapter'

enhanceGlobals()

Expand All @@ -52,11 +52,11 @@ export default function middlewareLoader(this: any) {
}

export default function (opts) {
return blockUnallowedResponse(adapter({
return adapter({
...opts,
page: ${JSON.stringify(page)},
handler,
}))
})
}
`
}
52 changes: 0 additions & 52 deletions packages/next/build/webpack/plugins/middleware-plugin.ts
Expand Up @@ -232,21 +232,6 @@ function isInMiddlewareLayer(parser: webpack.javascript.JavascriptParser) {
return parser.state.module?.layer === 'middleware'
}

function isInMiddlewareFile(parser: webpack.javascript.JavascriptParser) {
return (
parser.state.current?.layer === 'middleware' &&
/middleware\.\w+$/.test(parser.state.current?.rawRequest)
)
}

function isNullLiteral(expr: any) {
return expr.value === null
}

function isUndefinedIdentifier(expr: any) {
return expr.name === 'undefined'
}

function isProcessEnvMemberExpression(memberExpression: any): boolean {
return (
memberExpression.object?.type === 'Identifier' &&
Expand Down Expand Up @@ -345,14 +330,12 @@ function getCodeAnalyzer(params: {
dev: boolean
compiler: webpack.Compiler
compilation: webpack.Compilation
allowMiddlewareResponseBody: boolean
}) {
return (parser: webpack.javascript.JavascriptParser) => {
const {
dev,
compiler: { webpack: wp },
compilation,
allowMiddlewareResponseBody,
} = params
const { hooks } = parser

Expand Down Expand Up @@ -488,32 +471,6 @@ function getCodeAnalyzer(params: {
}
}

/**
* A handler for calls to `new Response()` so we can fail if user is setting the response's body.
*/
const handleNewResponseExpression = (node: any) => {
const firstParameter = node?.arguments?.[0]
if (
isInMiddlewareFile(parser) &&
firstParameter &&
!isNullLiteral(firstParameter) &&
!isUndefinedIdentifier(firstParameter)
) {
const error = buildWebpackError({
message: `Middleware is returning a response body (line: ${node.loc.start.line}), which is not supported.
Learn more: https://nextjs.org/docs/messages/returning-response-body-in-middleware`,
compilation,
parser,
...node,
})
if (dev) {
compilation.warnings.push(error)
} else {
compilation.errors.push(error)
}
}
}

/**
* Handler to store original source location of static and dynamic imports into module's buildInfo.
*/
Expand Down Expand Up @@ -568,10 +525,6 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`,
.tap(NAME, handleWrapWasmInstantiateExpression)
}

if (!allowMiddlewareResponseBody) {
hooks.new.for('Response').tap(NAME, handleNewResponseExpression)
hooks.new.for('NextResponse').tap(NAME, handleNewResponseExpression)
}
hooks.callMemberChain.for('process').tap(NAME, handleCallMemberChain)
hooks.expressionMemberChain.for('process').tap(NAME, handleCallMemberChain)
hooks.importCall.tap(NAME, handleImport)
Expand Down Expand Up @@ -835,23 +788,19 @@ export default class MiddlewarePlugin {
private readonly dev: boolean
private readonly sriEnabled: boolean
private readonly hasFontLoaders: boolean
private readonly allowMiddlewareResponseBody: boolean

constructor({
dev,
sriEnabled,
hasFontLoaders,
allowMiddlewareResponseBody,
}: {
dev: boolean
sriEnabled: boolean
hasFontLoaders: boolean
allowMiddlewareResponseBody: boolean
}) {
this.dev = dev
this.sriEnabled = sriEnabled
this.hasFontLoaders = hasFontLoaders
this.allowMiddlewareResponseBody = allowMiddlewareResponseBody
}

public apply(compiler: webpack.Compiler) {
Expand All @@ -864,7 +813,6 @@ export default class MiddlewarePlugin {
dev: this.dev,
compiler,
compilation,
allowMiddlewareResponseBody: this.allowMiddlewareResponseBody,
})
hooks.parser.for('javascript/auto').tap(NAME, codeAnalyzer)
hooks.parser.for('javascript/dynamic').tap(NAME, codeAnalyzer)
Expand Down
2 changes: 0 additions & 2 deletions packages/next/build/webpack/plugins/telemetry-plugin.ts
Expand Up @@ -38,7 +38,6 @@ export type Feature =
| `swc/target/${SWC_TARGET_TRIPLE}`
| 'turbotrace'
| 'transpilePackages'
| 'allowMiddlewareResponseBody'
| 'skipMiddlewareUrlNormalize'
| 'skipTrailingSlashRedirect'

Expand Down Expand Up @@ -101,7 +100,6 @@ const BUILD_FEATURES: Array<Feature> = [
'swc/target/aarch64-pc-windows-msvc',
'turbotrace',
'transpilePackages',
'allowMiddlewareResponseBody',
'skipMiddlewareUrlNormalize',
'skipTrailingSlashRedirect',
]
Expand Down
3 changes: 0 additions & 3 deletions packages/next/server/config-schema.ts
Expand Up @@ -7,9 +7,6 @@ const configSchema = {
type: 'object',
additionalProperties: false,
properties: {
allowMiddlewareResponseBody: {
type: 'boolean',
},
amp: {
additionalProperties: false,
properties: {
Expand Down
2 changes: 0 additions & 2 deletions packages/next/server/config-shared.ts
Expand Up @@ -504,8 +504,6 @@ export interface NextConfig extends Record<string, any> {
// A list of packages that should always be transpiled and bundled in the server
transpilePackages?: string[]

allowMiddlewareResponseBody?: boolean

skipMiddlewareUrlNormalize?: boolean

skipTrailingSlashRedirect?: boolean
Expand Down
12 changes: 0 additions & 12 deletions packages/next/server/config.ts
Expand Up @@ -615,18 +615,6 @@ function assignDefaults(dir: string, userConfig: { [key: string]: any }) {
result.transpilePackages = (result.experimental as any).transpilePackages
}

if (
result.experimental &&
'allowMiddlewareResponseBody' in (result.experimental as any)
) {
Log.warn(
`\`allowMiddlewareResponseBody\` has been moved out of \`experimental\`. Please update your ${configFileName} file accordingly.`
)
result.allowMiddlewareResponseBody = (
result.experimental as any
).allowMiddlewareResponseBody
}

if (
result.experimental &&
'skipMiddlewareUrlNormalize' in (result.experimental as any)
Expand Down
26 changes: 0 additions & 26 deletions packages/next/server/web/adapter.ts
Expand Up @@ -198,32 +198,6 @@ export async function adapter(params: {
}
}

export function blockUnallowedResponse(
promise: Promise<FetchEventResult>
): Promise<FetchEventResult> {
if (process.env.__NEXT_ALLOW_MIDDLEWARE_RESPONSE_BODY) {
return promise
}

return promise.then((result) => {
if (result.response?.body) {
console.error(
new Error(
`A middleware can not alter response's body. Learn more: https://nextjs.org/docs/messages/returning-response-body-in-middleware`
)
)
return {
...result,
response: new Response('Internal Server Error', {
status: 500,
statusText: 'Internal Server Error',
}),
}
}
return result
})
}

function getUnsupportedModuleErrorMessage(module: string) {
// warning: if you change these messages, you must adjust how react-dev-overlay's middleware detects modules not found
return `The edge runtime does not support Node.js '${module}' module.
Expand Down
1 change: 0 additions & 1 deletion packages/next/telemetry/events/build.ts
Expand Up @@ -167,7 +167,6 @@ export type EventBuildFeatureUsage = {
| 'build-lint'
| 'vercelImageGeneration'
| 'transpilePackages'
| 'allowMiddlewareResponseBody'
| 'skipMiddlewareUrlNormalize'
| 'skipTrailingSlashRedirect'
invocationCount: number
Expand Down
4 changes: 2 additions & 2 deletions test/development/middleware-warnings/index.test.ts
Expand Up @@ -61,9 +61,9 @@ describe('middlewares', () => {
return new Response(await fetch('https://example.vercel.sh'));
}`,
},
])('warns when $title', async ({ code }) => {
])('does not warn when $title', async ({ code }) => {
;({ cleanup } = await sandbox(next, new Map([[middlewarePath, code]])))
expect(next.cliOutput).toMatch(middlewareWarning)
expect(next.cliOutput).not.toMatch(middlewareWarning)
})

it.each([
Expand Down
1 change: 0 additions & 1 deletion test/e2e/app-dir/app-middleware/next.config.js
@@ -1,5 +1,4 @@
module.exports = {
allowMiddlewareResponseBody: true,
experimental: {
appDir: true,
},
Expand Down
14 changes: 6 additions & 8 deletions test/e2e/middleware-responses/test/index.test.ts
Expand Up @@ -29,25 +29,23 @@ describe('Middleware Responses', () => {
])
})

it(`${label}should fail when returning a stream`, async () => {
it(`${label}should not fail when returning a stream`, async () => {
const res = await fetchViaHTTP(next.url, `${locale}/stream-a-response`)
expect(res.status).toBe(500)
expect(res.status).toBe(200)

if (!(global as any).isNextDeploy) {
expect(await res.text()).toEqual('Internal Server Error')
expect(next.cliOutput).toContain(
expect(next.cliOutput).not.toContain(
`A middleware can not alter response's body. Learn more: https://nextjs.org/docs/messages/returning-response-body-in-middleware`
)
}
})

it(`${label}should fail when returning a text body`, async () => {
it(`${label}should not fail when returning a text body`, async () => {
const res = await fetchViaHTTP(next.url, `${locale}/send-response`)
expect(res.status).toBe(500)
expect(res.status).toBe(200)

if (!(global as any).isNextDeploy) {
expect(await res.text()).toEqual('Internal Server Error')
expect(next.cliOutput).toContain(
expect(next.cliOutput).not.toContain(
`A middleware can not alter response's body. Learn more: https://nextjs.org/docs/messages/returning-response-body-in-middleware`
)
}
Expand Down
1 change: 0 additions & 1 deletion test/e2e/skip-trailing-slash-redirect/app/next.config.js
@@ -1,6 +1,5 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
allowMiddlewareResponseBody: true,
skipMiddlewareUrlNormalize: true,
skipTrailingSlashRedirect: true,
async redirects() {
Expand Down
6 changes: 3 additions & 3 deletions test/integration/middleware-build-errors/test/index.test.js
Expand Up @@ -53,13 +53,13 @@ describe('Middleware validation during build', () => {
])('given a middleware $title', ({ code }) => {
beforeAll(() => writeFile(middlewareFile, code))

it('throws an error', async () => {
it('does not throw an error', async () => {
const { stderr, code } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})
expect(stderr).toMatch(middlewareError)
expect(code).toBe(1)
expect(stderr).not.toMatch(middlewareError)
expect(code).toBe(0)
})
})

Expand Down
@@ -1,5 +1,4 @@
module.exports = {
allowMiddlewareResponseBody: true,
skipMiddlewareUrlNormalize: true,
skipTrailingSlashRedirect: true,
}
4 changes: 0 additions & 4 deletions test/integration/telemetry/test/index.test.js
Expand Up @@ -1137,10 +1137,6 @@ describe('Telemetry CLI', () => {
stderr,
'NEXT_BUILD_FEATURE_USAGE'
)
expect(featureUsageEvents).toContainEqual({
featureName: 'allowMiddlewareResponseBody',
invocationCount: 1,
})
expect(featureUsageEvents).toContainEqual({
featureName: 'skipMiddlewareUrlNormalize',
invocationCount: 1,
Expand Down