Amazon CloudFront中的NextJS动态路由

wkftcu5l  于 2023-03-12  发布在  其他
关注(0)|答案(3)|浏览(173)

我有一个使用NextJS作为 Package 器的应用程序,我使用NextJS's dynamic routing feature。我在将它部署到CloudFront时遇到了一个问题,因为dns.com/path/page没有被渲染,而CloudFront期望它是dns.com/path/page.html。我通过应用这个lambda-edge-nice-url解决方案解决了这个问题。它现在工作正常。但是,还有一个问题:NextJS的动态路由. dsn.com/path/subpath/123应该可以工作,因为123是一个动态参数,但是它不起作用,In只在我访问dns.com/path/subpath/[id]时返回页面,当然这是不正确的,因为[id]不是我想要加载的参数。
最奇怪的是:如果我试图直接访问上面提到的URL,它会失败。2然而,在应用程序内部,我有一些按钮和链接可以重定向用户,并且可以正常工作。
从应用程序内部导航(回调中包含router.push的按钮):

尝试直接访问URL:

有人能帮助我正确地路由请求吗?

uqxowvwt

uqxowvwt1#

我使用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;
};
roejwanj

roejwanj2#

在尝试了许多不同的代码之后,我终于想出了一个Lambda边表达式,它集两个问题于一身:

  • 需要在URL末尾插入.html
  • NextJS动态路由在刷新或直接访问URL时不起作用。

下面的代码首先处理动态路由。它使用一个正则表达式来理解当前的URL,并将请求重定向到正确的[id].html文件。之后,如果没有一个正则表达式匹配,并且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的回答。由于某种原因,他的代码对我不起作用,但对某些人可能起作用,所以请查看他的回答。另外,向Manc大吼一声,lambda-edge-nice-urls repo的创建者。我的代码基本上是两者的混合。

v440hwme

v440hwme3#

@Pelicer提到的解决方案实际上不能扩展到他们的解决方案之外,并且限制了路径参数的命名方式。相反,类似的方法是使用动态生成的路由文件。使用NextJS,如果运行build命令,它将在out/.next/routes-manifest.json输出一个路由清单。该文件看起来如下所示

{
"version": 3,
"pages404": true,
"basePath": "",
"redirects": [
    {
        "source": "/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/",
        "destination": "/:file",
        "internal": true,
        "statusCode": 308,
        "regex": "^(?:/((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+))/$"
    },
    {
        "source": "/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)",
        "destination": "/:notfile/",
        "internal": true,
        "statusCode": 308,
        "regex": "^(?:/((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+))$"
    }
],
"headers": [],
"dynamicRoutes": [
    {
        "page": "/test-path/[testPathId]",
        "regex": "^/test\\-path/([^/]+?)(?:/)?$",
        "routeKeys": {
            "testPathId": "testPathId"
        },
        "namedRegex": "^/test\\-path/(?<testPathId>[^/]+?)(?:/)?$"
    }
],
"staticRoutes": [
    {
        "page": "/",
        "regex": "^/(?:/)?$",
        "routeKeys": {},
        "namedRegex": "^/(?:/)?$"
    },
    {
        "page": "/home",
        "regex": "^/home(?:/)?$",
        "routeKeys": {},
        "namedRegex": "^/home(?:/)?$"
    }
],
"dataRoutes": [],
"rsc": {
    "header": "RSC",
    "varyHeader": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"rewrites": []}

这为我们提供了nextjs生成的动态路由,用于静态生成的应用程序。然后,我们可以编写一个简单的CloudFront Lambda@Edge函数,在请求到来时快速Map请求。以下代码将读取上述json清单,并将请求重新路由到正确的S3路径。
注意:这里可以在静态路由和动态路由之间添加一些额外的重用。

exports.handler = function (event, context, callback) {
let routes = require('./routes-manifest.json');
const { request } = event.Records[0].cf;
const { uri } = request;
const {dynamicRoutes, staticRoutes} = routes;
const appendToDirs = 'index.html';

if(!uri || uri === '/' || uri === ''){
  callback(null, request);
  return;
}
dynamicRoutes.forEach(route => {
  if(uri.match(route.regex)){
    if(uri.charAt(-1) === "/"){
      request.uri = route.page + appendToDirs;
    } else {
      request.uri = route.page + "/" + appendToDirs;
    }
    callback(null, request);
    return;
  }
});
staticRoutes.forEach(route => {
  if(uri.match(route.regex)){
    if(uri.charAt(-1) === "/"){
      request.uri = route.page + appendToDirs;
    } else {
      request.uri = route.page + "/" + appendToDirs;
    }
    callback(null, request);
    return;
  }
});
// If nothing matches, return request unchanged
callback(null, request);};

相关问题