【NextJS】中间件实战介绍

news/2024/9/18 9:05:27/文章来源:https://www.cnblogs.com/o-O-oO/p/18378368

原创 洞窝技术

使用 Next.js 中间件实现高性能个性化

在当今的数字时代,用户期望获得量身定制的在线体验。个性化已经从一个奢侈品变成了必需品,尤其是对于希望在竞争激烈的市场中脱颖而出的企业来说。然而,实现高性能的个性化往往是一个挑战,需要在用户体验和系统性能之间取得平衡。

这就是 Next.js 中间件发挥作用的地方。作为 React 框架中的一个强大功能,Next.js 的中间件为开发者提供了一种优雅而高效的方式来实现个性化,同时保持应用程序的高性能。

本文将探讨如何利用 Next.js 中间件来创建动态、响应迅速且个性化的 web 应用。我们将深入研究中间件的工作原理,讨论其在实现个性化中的优势,并提供实际的实现步骤和优化技巧。无论你是经验丰富的 Next.js 开发者,还是刚刚开始探索这个强大框架的新手,本文都将为你提供有价值的见解,帮助你提升用户体验并保持网站的高性能。

什么是Next.js中间件

先来看看官网的解释:中间件允许你在请求完成之前运行代码。然后,根据传入的请求,你可以通过重写、重定向、修改请求或响应标头或直接响应来修改响应。

Next.js 中间件是在 Next.js 12版本中引入的概念,并在后续版本中得到增强。它允许开发者在请求被 Next.js 路由处理之前拦截这些请求,并执行自定义的服务器端和/或客户端代码。

这些中间件可以运行在两个不同的阶段:

Server-side: 当请求初始到达服务器时;Client-side: 在客户端导航到不同页面时(例如,当使用 next/link 或 next/router)。

中间件的作用

Next.js 中间件的作用非常多样化,根据用户的喜好定制内容可以极大地改善用户在您网站上的体验,最终可以提高您的转化率。通过解析 HTTP 请求标头,您可以向用户提供不同版本的网站。例如:

国际化: 基于请求头或 URL 参数调整内容以适应不同的区域和语言。重写和重定向: 基于请求动态重写 URL 路径或进行重定向。A/B 测试: 根据某些逻辑展示不同版本的页面。地理位置:根据用户的区域或地理位置(即由 IP 地址确定),您可以翻译文本、显示本地相关内容以及显示以当地货币定制的价格。用户信息:一旦用户登录,您就可以设置包含有关用户信息的 cookie,例如他们的性别或他们在您的网站上购物的频率。技术栈:从浏览器的用户代理,您可以向iPhone或Android用户呈现不同的内容,例如下载链接。回访用户:通过在首次访问时设置 cookie,后续访问可以向回访用户显示特殊内容,例如诱人的促销信息。身份验证和授权:检查用户的身份验证状态和权限。如果用户没有适当的权限,可以在中间件中阻止请求继续进行,并返回适当的错误响应。

当然您还可以发掘他更多的用法。

如何使用中间件

在根目录下创建 middleware.js或middleware.ts。(与 pages 或 app 处于同一级别)这个文件是特殊的,Next.js 会自动将其识别为中间件。

1. 请求到达服务器

当一个请求到达 Next.js 服务器时,服务器会首先检查是否有任何中间件需要执行。

2. 中间件执行

中间件是一个函数,它接受 request(请求对象)和 response(响应对象)作为参数,并且可以对它们进行操作。中间件函数的签名通常是这样的:

export default function middleware(request, response) {// 你的中间件逻辑
}

3. 中间件的配置

中间件可以通过 next.config.js 文件进行配置,指定在哪些路径下生效。例如:

// next.config.js
module.exports = {async middleware(request, response) {// 在这里导入并使用你的中间件const { middleware } = require('./middleware');return middleware(request);},async rewrites() {return [{source: '/:path*',destination: '/:path*',},]},
}

在这个配置中,中间件将应用于所有路径(/:path*),你可以根据需要调整路径匹配模式来限制中间件的应用范围。
例如,如果你只想让中间件在 /protected 路径下生效,可以这样配置:

// next.config.js
module.exports = {async rewrites() {return [{source: '/protected/:path*',destination: '/protected/:path*',},];},async middleware(request, response) {// 在这里导入并使用你的中间件const { middleware } = require('./middleware');return middleware(request);},
};

4. 请求处理和响应修改

在中间件中,你可以对请求进行各种处理,例如:身份验证,重定向等等
例如,一个简单的重定向中间件可能是这样的:

export default function middleware(request, response) {if (!request.cookies.token) {// 如果用户没有 token,则重定向到登录页面return response.redirect('/login');}// 继续处理请求
}

5. 继续处理或结束请求

中间件可以决定是否继续处理请求或直接结束请求。
如果中间件调用了 next() 函数,表示继续传递给下一个中间件或最终的页面处理函数。
如果中间件直接返回了响应,则请求处理到此结束,不再继续传递。

6. 异步中间件

中间件可以是异步函数,允许你进行异步操作,例如数据库查询、外部 API 调用等:

export default async function middleware(request, respons) {const user = await fetchUserFromDatabase(request.cookies.token);if (!user) {return respons.redirect('/login');}// 继续处理请求
}

7. 错误处理

中间件中出现的错误可以通过捕获异常来处理,确保不会因为错误导致整个请求崩溃:

export default async function middleware(request, respons) {try {const user = await fetchUserFromDatabase(request.cookies.token);if (!user) {return respons.redirect('/login');}// 继续处理请求} catch (error) {console.error('Middleware error:', error);return respons.status(500).send('Internal Server Error');}
}

通过中间件,Next.js 提供了一种灵活且强大的方式来处理请求,使你可以在请求到达页面或 API 路由之前进行各种操作。

import { NextResponse } from 'next/server';export function middleware(request) {// 例如,检查用户是否已认证const isAuthenticated = checkUserAuthentication(request);if (!isAuthenticated) {// 如果用户未认证,重定向到登录页面return NextResponse.redirect('/login');}// 如果用户已认证,继续处理请求return NextResponse.next();
}function checkUserAuthentication(request) {// 自定义的身份验证逻辑// 例如,检查请求头部中的认证信息const authHeader = request.headers.get('Authorization');return authHeader === 'Bearer valid-token';
}

在这个示例中,中间件检查用户是否已认证,如果未认证,则重定向到登录页面。如果已认证,则继续处理请求。

使用中间件可以让您在 Next.js 应用中更灵活地处理请求和响应,从而实现更复杂和定制化的功能。

中间件使我们能够完全在Edge Runtime(边缘运行时)实现个性化。中间件根据 HTTP 请求标头中的数据,将不同的用户重定向到应用程序的不同版本,例如我们根据客户端的当前IP将网站重定向到不同国家的网站。上图显示了 Next.js 中间件如何工作。

在 Next.js 上实现个性化

下面结合我们的真实业务场景,来看看如何根据客户端的当前的IP地址将网站重定向到不同国家的网站。
我们的网站根据国家不同配置了不一样的营销活动和商品,我们要求通过url做区分,例如新加坡 www.xxx.com/sg, 泰国 www.xxx.com/th, 看到这里,使用过nextjs的小伙伴应该已经有一个疑问了,在地址里写/sg,next会去pages里找名为sg的文件,如果不作任何处理,那你直接访问就会报找不到对应的路由。

想要解决有两种方案,1.修改你的pages目录结构,在pages目录下创建一个[country]文件夹,这样你的目录就会变成pages/[country]/xxx; 这种方案虽然能实现,但是不是有点麻烦。

接下来我们看利用nextjs的中间件怎么解决,上代码

// middleware.js
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { countryCodes } from '@/utils/country'; // countryCodes['sg', 'my',...] export function middleware(request: NextRequest) {try {const { pathname } = request.nextUrl;const pathParts = pathname.split('/').filter(Boolean);// 没有有国家代码,默认sgif (!countryCodes.includes(pathParts[0])) {const defaultCountry = 'sg';return NextResponse.redirect(new URL(`/${defaultCountry}${pathname}`, request.url));}// 重写 URL,移除国家代码const newPathname = '/' + pathParts.slice(1).join('/');return NextResponse.rewrite(new URL(newPathname, request.url));} catch (error) {console.error('Error in middleware:', error);return NextResponse.error();}
}// Configuration to specify the paths the middleware should match
export const config = {matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)',],
};

上面这段代码会检查路径的第一个部分是否是一个有效的国家代码。如果不是,则默认使用 'sg'(新加坡)作为国家代码,并重定向到新的 URL。如果路径中包含有效的国家代码,则将其移除,并重写请求的 URL。
运行一下会发现,已经实现了我们想要的效果,已经不会报路由错误了,每个页面访问地址前都增加了国家代码,但是新的问题又来了,你会发现页面是空白的,打开控制台你会发现你的静态资源和接口请求都自动拼接了国家代码,这显然是不对的,下面让我们改动一下,加入下面的代码

// 不处理 Next.js 内部请求和静态文件if (pathname.startsWith('/_next') || pathname.startsWith('/static') || pathname.includes('/dowo-app-application/') ||pathname.includes('/dowo-ops-web-application/') ||pathname.includes('/comment/') ||pathname.includes('.')) {return NextResponse.next()}

这样再看,页面就正常了,以为到这儿就完事了吗?小伙伴突然过来和我说,服务端接口请求都拿不到参数了,还得在改进一下

    const { pathname, searchParams } = request.nextUrl;//将searchParams拼在地址后面

到这里功能实现完成了,点一点都正常了。结果产品突然说我们想要根据当前的访问用户的ip自动去跳转对应的国家,还得再修改一下,整体思路就是,先判断有没有国家代码,如果没有就获取当前的用户ip,通过ip去获取所在国家的代码,获取到之后判断是否是在我们所支持的范围内,如果不支持还按新加坡处理,下面来看看完整的代码吧

// middleware.js
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { countryCodes } from '@/utils/country'export async function middleware(request: NextRequest) {const { pathname, searchParams } = request.nextUrl;// 不处理 Next.js 内部请求和静态文件if (pathname.startsWith('/_next') || pathname.startsWith('/static') || pathname.includes('/easyhome-app-application/') ||pathname.includes('/easyhome-ops-web-application/') ||pathname.includes('/api_config/') ||pathname.includes('/comment/') ||pathname.includes('.')) {return NextResponse.next()}const pathParts = pathname.split('/').filter(Boolean)if (!countryCodes.includes(pathParts[0])) { //  国家代码不存在let defaultCountry = 'sg';// 获取 IP 地址try {const ip = (request.ip || request.headers.get('x-real-ip') || request.headers.get('x-forwarded-for') || '').split(',')[0].trim();const response = await fetch(`${request.nextUrl.origin}/api/ipLookup?ip=${ip}`);if (!response.ok) {return NextResponse.redirect(new URL(`/${defaultCountry}${pathname}?${searchParams.toString()}`, request.url))}const ipInfo = await response.json();if (ipInfo && ipInfo.country) {const code = ipInfo.country.iso_code.toLowerCase();if (countryCodes.includes(code)) {defaultCountry = code;}}return NextResponse.redirect(new URL(`/${defaultCountry}${pathname}?${searchParams.toString()}`, request.url))} catch (error) {console.error('Error fetching IP info:', error);return NextResponse.redirect(new URL(`/${defaultCountry}${pathname}?${searchParams.toString()}`, request.url))  }}// 重写 URL,移除国家代码const newPathname = '/' + pathParts.slice(1).join('/') + '?' + searchParams.toString();return NextResponse.rewrite(new URL(newPathname, request.url))
}export const config = {matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)",],
};

到这里,我们的功能就全部完成了,可以通过用户的ip地址自动跳转对应的国家。
总结

借助 Next.js 中间件,我们如今能够以一种对开发者来说相对简单、对用户来说高性能的方式实现个性化功能。Next.js 的中间件为我们提供了一个强大的扩展点,能够在请求处理和页面渲染之间进行干预。它的应用不仅限于身份验证和重定向,还可以处理任何需要在请求生命周期特定时刻介入的逻辑。

Next.js 团队通过引入中间件这一概念,进一步增强了框架的灵活性,使开发者能够以最少的配置实现复杂的请求处理策略。随着 Next.js 的不断演进,我们可以期待中间件变得更加强大,为开发者提供更多的可能性。

参考:next官网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/786761.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

排列组合问题

排列公式 从 \(n\) 个数中选出 \(m\) 个数并且排序。 公式推导: \[ A^2_3 = 3 \times 2 = 6\\3_6 = 6 \times 5 \times 4 = 120\\ A^2_6 = 6 \times 5 = 30\\ \therefore A^m_n = n(n-1)(n-2)\dots (n-m+1)\\ 又\because n!=n\times (n-1)\times (n-2) \dots \times 2\times…

Docker简介及安装

本系列将会与大家分享 Docker 的相关知识。本章主要简要介绍 Docker,并指导大家如何在 CentOS 7 上进行 Docker 的安装。本系列将会与大家分享 Docker 的相关知识。本章主要简要介绍 Docker,并指导大家如何在 CentOS 7 上进行 Docker 的安装。废话不多说,下面我们直接进入主…

C程序设计语言(第2版新版)练习题1-9

练习1-9 编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替。#include <stdio.h>int main(int argc, char *argv[]) {(void)argc;(void)argv;int c;int c_last = \0;while((c = getchar()) != EOF) {if (( != c) || ( != c_last)) {putchar(c);}c…

C# .NET CORE 知识点总结【基础篇】

心之所向,勇往直前!记录面试中的那些小事。面试题只是一道门,最好还是走进屋里看看。正文 结语本篇到此结束,如果有任何疑问或者指正,请发表在评论区。

相遇(容斥+最短路+分类,水紫)

第5题 相遇 查看测评数据信息给定一个有n个节点m条边的无向图,在某一时刻节点st上有一个动点a, 节点end上有一个动点b, 动点a向节点end方向移动,要求是尽快到达end点,与此同时,动点b向节点st方向移动,要求是尽快到达st点, 但是整个过程中a和b不能相遇,问两点不相遇一…

NYX靶机笔记

NYX靶机笔记 概述 VulnHub里的简单靶机 靶机地址:https://download.vulnhub.com/nyx/nyxvm.zip 1、nmap扫描 1)主机发现 # -sn 只做ping扫描,不做端口扫描 nmap -sn 192.168.84.1/24 # 发现靶机ip为 MAC Address: 00:50:56:E0:D5:D4 (VMware) Nmap scan report for 192.168.…

VS2022 Visual Studio Installer 一直卡在0%,或者下载速度慢的问题解决办法

vs2022 installer,安装失败的问题C:\Users\Administrator\AppData\Local\Temp到c盘查看日志,发现是下载一个叫 vs_installer.opc的东西失败了,直接复制日志里的https://aka.ms/vs/17/release/installer,下载,发现成功下载,然后放到installer安装器同级目录,重新打开setu…

[Java基础]虚拟线程

虚拟线程(Virtual Thread)是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process,LWP),由 JVM 调度。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。 虚拟线程和平台线程有什么关系? 在引入虚拟线程之前,java.lang.Thread 包已经…

一台电脑配置两个Git账号(github和gitlab),不同仓库使用不同的git

我们工作时一般都是使用gitlab,工作电脑也一般配置的 git 是连接 gitlab 的,那么当我们如果想用同一个电脑实现不同仓库根据自己的需要到底是推送代码到github还是 gitlab,以及使用哪个账号,(比如如果想用工作电脑维护自己的 github 项目,但是怕后面被公司追究用工作电脑干…

DDD是软件工程的第一性原理?

本文书接上回《DDD建模后写代码的正确姿势》,关注公众号(老肖想当外语大佬)获取信息:最新文章更新;DDD框架源码(.NET、Java双平台);加群畅聊,建模分析、技术实现交流;视频和直播在B站。前提 本文需要以系列前文的逻辑链条和结论为前提,如果没有阅读过前文的,可以阅…

[TCP/IP]三次握手

三次握手TCP建立连接为什么需要三次握手 TCP作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率,而用三次恰恰可以满足以上两方面的需求! TCP可靠传输的精髓:TCP连接的一方A,由操作系统动态随机选取一个32位长的序列号(Initial Sequence Nu…