Amazon CloudFront 中的 NextJS 动态路由
NextJS dynamic routing in Amazon CloudFront
我有一个使用 NextJS 作为包装器的应用程序,我使用了 NextJS's dynamic routing feature. I had a problem when deploying it to CloudFront due to dns.com/path/page
not being rendered, instead CloudFront expected it to be dns.com/path/page.html
. I worked it around by applying this lambda-edge-nice-url 解决方案。它现在工作正常。但是,仍然存在一个问题:NextJS 的动态路由。 dsn.com/path/subpath/123
应该有效,因为 123 是一个动态参数。但是,这不起作用。在我访问dns.com/path/subpath/[id]
时的页面只有returns,这当然是不正确的,因为[id]不是我要加载的参数。
最奇怪的是:如果我尝试直接访问 URL,就像我上面所说的那样,它会失败。但是,在应用程序内部,我有重定向用户的按钮和链接,并且可以正常工作。
从应用程序内部导航(回调中带有 router.push
的按钮):
尝试直接访问 url:
谁能帮我正确路由请求?
我使用 CloudFront Lambda@Edge 源请求函数来处理将我的动态路由和静态路由重写到适当的 HTML 文件,以便 CloudFront 可以为任何路径提供预期文件。
我的 lambda 函数看起来像
export const handler: CloudFrontRequestHandler = async (event) => {
const eventRecord = event.Records[0];
const request = eventRecord.cf.request;
const uri = request.uri;
// handle /posts/[id] dynamic route
if (uri === '/posts' || uri.startsWith('/posts/')) {
request.uri = "/posts/[id].html";
return request;
}
// if URI includes ".", indicates file extension, return early and don't modify URI
if (uri.includes('.')) {
return request;
}
// if URI ends with "/" slash, then we need to remove the slash first before appending .html
if (uri.endsWith('/')) {
request.uri = request.uri.substring(0, request.uri.length - 1);
}
request.uri += '.html';
return request;
};
在尝试了很多不同的代码之后,我终于想出了一个 Lambda 边缘表达式,将两个问题合二为一:
- 需要在URL
末尾插入.html
- 无法刷新或直接访问 URL.
的 NextJS 动态路由
下面的代码基本上首先处理了动态路由。它使用正则表达式来理解当前的 URL 并将请求重定向到正确的 [id].html
文件。之后,如果正则表达式的 none 匹配,并且 URL 不包含 .html
扩展名,它会添加扩展名并检索正确的文件。
const config = {
suffix: '.html',
appendToDirs: 'index.html',
removeTrailingSlash: false,
};
const regexSuffixless = /\/[^/.]+$/; // e.g. "/some/page" but not "/", "/some/" or "/some.jpg"
const regexTrailingSlash = /.+\/$/; // e.g. "/some/" or "/some/page/" but not root "/"
const dynamicRouteRegex = /\/subpath\/\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/; // e.g /urs/some-uuid; // e.g. '/subpath/uuid'
exports.handler = function handler(event, context, callback) {
const { request } = event.Records[0].cf;
const { uri } = request;
const { suffix, appendToDirs, removeTrailingSlash } = config;
//Checks for dynamic route and retrieves the proper [id].html file
if (uri.match(dynamicRouteRegex)) {
request.uri = "/subpath/[id].html";
callback(null, request);
return;
}
// Append ".html" to origin request
if (suffix && uri.match(regexSuffixless)) {
request.uri = uri + suffix;
callback(null, request);
return;
}
// Append "index.html" to origin request
if (appendToDirs && uri.match(regexTrailingSlash)) {
request.uri = uri + appendToDirs;
callback(null, request);
return;
}
// Redirect (301) non-root requests ending in "/" to URI without trailing slash
if (removeTrailingSlash && uri.match(/.+\/$/)) {
const response = {
// body: '',
// bodyEncoding: 'text',
headers: {
'location': [{
key: 'Location',
value: uri.slice(0, -1)
}]
},
status: '301',
statusDescription: 'Moved Permanently'
};
callback(null, response);
return;
}
// If nothing matches, return request unchanged
callback(null, request);
};
非常感谢@LongZheng 的回答。出于某种原因,他的代码对我不起作用,但可能对某些人有用,所以请检查他的答案。此外,还要特别感谢 lambda-edge-nice-urls repo 的创造者 Manc。我的代码基本上是两者的混合。
我有一个使用 NextJS 作为包装器的应用程序,我使用了 NextJS's dynamic routing feature. I had a problem when deploying it to CloudFront due to dns.com/path/page
not being rendered, instead CloudFront expected it to be dns.com/path/page.html
. I worked it around by applying this lambda-edge-nice-url 解决方案。它现在工作正常。但是,仍然存在一个问题:NextJS 的动态路由。 dsn.com/path/subpath/123
应该有效,因为 123 是一个动态参数。但是,这不起作用。在我访问dns.com/path/subpath/[id]
时的页面只有returns,这当然是不正确的,因为[id]不是我要加载的参数。
最奇怪的是:如果我尝试直接访问 URL,就像我上面所说的那样,它会失败。但是,在应用程序内部,我有重定向用户的按钮和链接,并且可以正常工作。
从应用程序内部导航(回调中带有 router.push
的按钮):
尝试直接访问 url:
谁能帮我正确路由请求?
我使用 CloudFront Lambda@Edge 源请求函数来处理将我的动态路由和静态路由重写到适当的 HTML 文件,以便 CloudFront 可以为任何路径提供预期文件。
我的 lambda 函数看起来像
export const handler: CloudFrontRequestHandler = async (event) => {
const eventRecord = event.Records[0];
const request = eventRecord.cf.request;
const uri = request.uri;
// handle /posts/[id] dynamic route
if (uri === '/posts' || uri.startsWith('/posts/')) {
request.uri = "/posts/[id].html";
return request;
}
// if URI includes ".", indicates file extension, return early and don't modify URI
if (uri.includes('.')) {
return request;
}
// if URI ends with "/" slash, then we need to remove the slash first before appending .html
if (uri.endsWith('/')) {
request.uri = request.uri.substring(0, request.uri.length - 1);
}
request.uri += '.html';
return request;
};
在尝试了很多不同的代码之后,我终于想出了一个 Lambda 边缘表达式,将两个问题合二为一:
- 需要在URL 末尾插入
- 无法刷新或直接访问 URL. 的 NextJS 动态路由
.html
下面的代码基本上首先处理了动态路由。它使用正则表达式来理解当前的 URL 并将请求重定向到正确的 [id].html
文件。之后,如果正则表达式的 none 匹配,并且 URL 不包含 .html
扩展名,它会添加扩展名并检索正确的文件。
const config = {
suffix: '.html',
appendToDirs: 'index.html',
removeTrailingSlash: false,
};
const regexSuffixless = /\/[^/.]+$/; // e.g. "/some/page" but not "/", "/some/" or "/some.jpg"
const regexTrailingSlash = /.+\/$/; // e.g. "/some/" or "/some/page/" but not root "/"
const dynamicRouteRegex = /\/subpath\/\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/; // e.g /urs/some-uuid; // e.g. '/subpath/uuid'
exports.handler = function handler(event, context, callback) {
const { request } = event.Records[0].cf;
const { uri } = request;
const { suffix, appendToDirs, removeTrailingSlash } = config;
//Checks for dynamic route and retrieves the proper [id].html file
if (uri.match(dynamicRouteRegex)) {
request.uri = "/subpath/[id].html";
callback(null, request);
return;
}
// Append ".html" to origin request
if (suffix && uri.match(regexSuffixless)) {
request.uri = uri + suffix;
callback(null, request);
return;
}
// Append "index.html" to origin request
if (appendToDirs && uri.match(regexTrailingSlash)) {
request.uri = uri + appendToDirs;
callback(null, request);
return;
}
// Redirect (301) non-root requests ending in "/" to URI without trailing slash
if (removeTrailingSlash && uri.match(/.+\/$/)) {
const response = {
// body: '',
// bodyEncoding: 'text',
headers: {
'location': [{
key: 'Location',
value: uri.slice(0, -1)
}]
},
status: '301',
statusDescription: 'Moved Permanently'
};
callback(null, response);
return;
}
// If nothing matches, return request unchanged
callback(null, request);
};
非常感谢@LongZheng 的回答。出于某种原因,他的代码对我不起作用,但可能对某些人有用,所以请检查他的答案。此外,还要特别感谢 lambda-edge-nice-urls repo 的创造者 Manc。我的代码基本上是两者的混合。