未验证 提交 4782bda3 编写于 作者: J JJ Kasper 提交者: GitHub

Add support for notFound in getServerSideProps (#18241)

This is a follow-up to https://github.com/vercel/next.js/pull/17755 which adds support for returning `notFound` to `getServerSideProps` also
上级 3f22490a
......@@ -772,7 +772,7 @@ const nextServerlessLoader: loader.Loader = function () {
if (!renderMode) {
if (_nextData || getStaticProps || getServerSideProps) {
if (renderOpts.ssgNotFound) {
if (renderOpts.isNotFound) {
res.statusCode = 404
const NotFoundComponent = ${
......
......@@ -263,7 +263,7 @@ export default async function exportPage({
html = (result as any).html
}
if (!html && !(curRenderOpts as any).ssgNotFound) {
if (!html && !(curRenderOpts as any).isNotFound) {
throw new Error(`Failed to render serverless page`)
}
} else {
......@@ -318,7 +318,7 @@ export default async function exportPage({
html = await renderMethod(req, res, page, query, curRenderOpts)
}
}
results.ssgNotFound = (curRenderOpts as any).ssgNotFound
results.ssgNotFound = (curRenderOpts as any).isNotFound
const validateAmp = async (
rawAmpHtml: string,
......
......@@ -1351,7 +1351,7 @@ export default class Server {
html = renderResult.html
pageData = renderResult.renderOpts.pageData
sprRevalidate = renderResult.renderOpts.revalidate
isNotFound = renderResult.renderOpts.ssgNotFound
isNotFound = renderResult.renderOpts.isNotFound
} else {
const origQuery = parseUrl(req.url || '', true).query
const resolvedUrl = formatUrl({
......@@ -1393,7 +1393,7 @@ export default class Server {
// TODO: change this to a different passing mechanism
pageData = (renderOpts as any).pageData
sprRevalidate = (renderOpts as any).revalidate
isNotFound = (renderOpts as any).ssgNotFound
isNotFound = (renderOpts as any).isNotFound
}
return { html, pageData, sprRevalidate, isNotFound }
......
......@@ -643,7 +643,7 @@ export async function renderToHTML(
)
}
;(renderOpts as any).ssgNotFound = true
;(renderOpts as any).isNotFound = true
;(renderOpts as any).revalidate = false
return null
}
......@@ -753,21 +753,35 @@ export async function renderToHTML(
}
const invalidKeys = Object.keys(data).filter(
(key) => key !== 'props' && key !== 'unstable_redirect'
(key) =>
key !== 'props' &&
key !== 'unstable_redirect' &&
key !== 'unstable_notFound'
)
if (invalidKeys.length) {
throw new Error(invalidKeysMsg('getServerSideProps', invalidKeys))
}
if ('unstable_notFound' in data) {
if (pathname === '/404') {
throw new Error(
`The /404 page can not return unstable_notFound in "getStaticProps", please remove it to continue!`
)
}
;(renderOpts as any).isNotFound = true
return null
}
if (
data.unstable_redirect &&
'unstable_redirect' in data &&
typeof data.unstable_redirect === 'object'
) {
checkRedirectValues(data.unstable_redirect, req)
if (isDataReq) {
data.props = {
;(data as any).props = {
__N_REDIRECT: data.unstable_redirect.destination,
}
} else {
......@@ -778,7 +792,11 @@ export async function renderToHTML(
if (
(dev || isBuildTimeSSG) &&
!isSerializableProps(pathname, 'getServerSideProps', data.props)
!isSerializableProps(
pathname,
'getServerSideProps',
(data as any).props
)
) {
// this fn should throw an error instead of ever returning `false`
throw new Error(
......@@ -786,7 +804,7 @@ export async function renderToHTML(
)
}
props.pageProps = Object.assign({}, props.pageProps, data.props)
props.pageProps = Object.assign({}, props.pageProps, (data as any).props)
;(renderOpts as any).pageData = props
}
} catch (dataFetchError) {
......
......@@ -132,10 +132,16 @@ export type GetServerSidePropsContext<
locales?: string[]
}
export type GetServerSidePropsResult<P> = {
props?: P
unstable_redirect?: Redirect
}
export type GetServerSidePropsResult<P> =
| {
props: P
}
| {
unstable_redirect: Redirect
}
| {
unstable_notFound: true
}
export type GetServerSideProps<
P extends { [key: string]: any } = { [key: string]: any },
......
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getServerSideProps = ({ query }) => {
if (query.hiding) {
return {
unstable_notFound: true,
}
}
return {
props: {
hello: 'world',
},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getServerSideProps = ({ query }) => {
if (query.hiding) {
return {
unstable_notFound: true,
}
}
return {
props: {
hello: 'world',
},
}
}
......@@ -118,6 +118,24 @@ const expectedManifestRoutes = () => [
),
page: '/non-json',
},
{
dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/not-found.json$`,
page: '/not-found',
},
{
dataRouteRegex: `^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/not\\-found\\/([^\\/]+?)\\.json$`,
namedDataRouteRegex: `^/_next/data/${escapeRegex(
buildId
)}/not\\-found/(?<slug>[^/]+?)\\.json$`,
page: '/not-found/[slug]',
routeKeys: {
slug: 'slug',
},
},
{
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/refresh.json$`
......@@ -235,6 +253,50 @@ const navigateTest = (dev = false) => {
const runTests = (dev = false) => {
navigateTest(dev)
it('should render 404 correctly when notFound is returned (non-dynamic)', async () => {
const res = await fetchViaHTTP(appPort, '/not-found', { hiding: true })
expect(res.status).toBe(404)
expect(await res.text()).toContain('This page could not be found')
})
it('should render 404 correctly when notFound is returned client-transition (non-dynamic)', async () => {
const browser = await webdriver(appPort, '/')
await browser.eval(`(function() {
window.beforeNav = 1
window.next.router.push('/not-found?hiding=true')
})()`)
await browser.waitForElementByCss('h1')
expect(await browser.elementByCss('html').text()).toContain(
'This page could not be found'
)
expect(await browser.eval('window.beforeNav')).toBe(null)
})
it('should render 404 correctly when notFound is returned (dynamic)', async () => {
const res = await fetchViaHTTP(appPort, '/not-found/first', {
hiding: true,
})
expect(res.status).toBe(404)
expect(await res.text()).toContain('This page could not be found')
})
it('should render 404 correctly when notFound is returned client-transition (dynamic)', async () => {
const browser = await webdriver(appPort, '/')
await browser.eval(`(function() {
window.beforeNav = 1
window.next.router.push('/not-found/first?hiding=true')
})()`)
await browser.waitForElementByCss('h1')
expect(await browser.elementByCss('html').text()).toContain(
'This page could not be found'
)
expect(await browser.eval('window.beforeNav')).toBe(null)
})
it('should SSR normal page correctly', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/hello.*?world/)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册