Next.js 实战 (十):中间件的魅力,打造更快更安全的应用

简介: 这篇文章介绍了什么是Next.js中的中间件以及其应用场景。中间件可以用于处理每个传入请求,比如实现日志记录、身份验证、重定向、CORS配置等功能。文章还提供了一个身份验证中间件的示例代码,以及如何使用限流中间件来限制同一IP地址的请求次数。中间件相当于一个构建模块,能够简化HTTP请求的预处理和后处理,提高代码的可维护性,有助于创建快速、安全和用户友好的Web体验。

什么是中间件?

Next.js 中,中间件(Middleware)是一种用于处理每个传入请求的功能。它允许你在请求到达页面之前对其进行修改或响应。

通过中间件,你可以实现诸如日志记录身份验证重定向CORS配置压缩等任务。中间件是构建高效和安全的 web 应用的重要组成部分。

应用场景

身份验证

你可以在中间件中检查用户的身份验证状态,比如从cookie或头部信息中读取JWT令牌,并根据验证结果决定是否允许访问特定页面或API端点。如果验证失败,可以返回401未授权状态码或者重定向到登录页面。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';

export function middleware(request) {
   
  const token = request.cookies.get('authToken')?.value;

  if (!token) {
   
    // 如果没有令牌,则重定向到登录页面
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 继续处理链
  return NextResponse.next();
}

日志记录

中间件非常适合用来记录所有进入应用程序的请求。这可以帮助监控流量模式、诊断问题以及了解用户行为。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';

export function middleware(request) {
   
  console.log(`Request to ${
     request.url} at ${
     new Date().toISOString()}`);

  // 继续处理链
  return NextResponse.next();
}

请求/响应转换

可以在请求到达最终目的地之前对请求数据进行预处理,也可以在发送给客户端之前修改响应内容。例如,格式化数据、添加额外的HTTP头、压缩响应体等。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';

export async function middleware(request) {
   
  // 修改请求体(假设是JSON格式)
  let modifiedBody = await request.json();
  modifiedBody.message = "Modified message";

  // 创建新的请求实例
  const newReq = new Request(request, {
   
    body: JSON.stringify(modifiedBody),
  });

  // 获取响应并修改它
  const response = await fetch(request.nextUrl.pathname, {
   
    method: request.method,
    headers: request.headers,
    body: newReq.body,
  });

  const resClone = await response.clone();
  const data = await resClone.json();
  data.newField = "This is a new field"; // 添加新字段

  // 返回修改后的响应
  return new Response(JSON.stringify(data), {
   
    status: response.status,
    headers: response.headers,
  });
}

重定向

基于某些条件(如URL路径、查询参数、用户代理等),你可以使用中间件来执行重定向操作,将用户引导至不同的页面。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';

export function middleware(request) {
   
  // 如果访问的是旧路径,则重定向到新路径
  if (request.nextUrl.pathname.startsWith('/login')) {
   
    return NextResponse.redirect(new URL('/dashboard', request.url));
  }

  // 继续处理链
  return NextResponse.next();
}

CORS(跨源资源共享)

设置适当的CORS头以允许或限制来自其他域的请求,这对于拥有多个子域名或需要与第三方服务交互的应用非常重要。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';

export function middleware(request) {
   
  const response = NextResponse.next();

  // 设置CORS头
  response.headers.set('Access-Control-Allow-Origin', '*');
  response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  return response;
}

限流

防止滥用API接口,可以通过中间件实现速率限制,限制同一IP地址在一定时间内的请求次数。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';
import rateLimit from 'express-rate-limit'; // 需要安装依赖

const limiter = rateLimit({
   
  windowMs: 1 * 60 * 1000, // 1分钟
  max: 100, // 每分钟最多100次请求
});

export function middleware(request) {
   
  return new Promise((resolve, reject) => {
   
    limiter(request, {
   }, (err) => {
   
      if (err) {
   
        return reject(new NextResponse('Too many requests', {
    status: 429 }));
      }
      resolve(NextResponse.next());
    });
  });
}

国际化路由

根据用户的语言偏好或地理位置自动调整网站的语言版本。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';

export function middleware(request) {
   
  const locale = request.cookies.get('NEXT_LOCALE')?.value || 'en';

  // 如果不是根路径并且没有包含语言前缀,则添加语言前缀
  if (!request.nextUrl.pathname.startsWith(`/${
     locale}`)) {
   
    const url = new URL(request.url);
    url.pathname = `/${
     locale}${
     url.pathname}`;
    return NextResponse.redirect(url);
  }

  // 继续处理链
  return NextResponse.next();
}

静态文件服务

尽管Next.js已经提供了基本的静态文件服务功能,但你可以用中间件来增强这一功能,比如为特定类型的文件提供自定义处理逻辑。
示例代码:

// middleware.js
import {
    NextResponse } from 'next/server';
import fs from 'fs/promises';

export async function middleware(request) {
   
  const path = request.nextUrl.pathname;

  if (path.startsWith('/static-files/')) {
   
    try {
   
      const filePath = `./public${
     path}`;
      const file = await fs.readFile(filePath);
      return new Response(file, {
   
        headers: {
    'Content-Type': 'application/octet-stream' },
      });
    } catch (error) {
   
      return NextResponse.next();
    }
  }

  // 继续处理链
  return NextResponse.next();
}

config 配置对象

matcher

matcher 是一个非常强大的配置项,它允许你指定中间件应该应用于哪些路径。你可以通过正则表达式或通配符模式来匹配URL路径。

  • 通配符:可以使用 * 来表示任意字符序列。
  • 正则表达式:支持完整的正则表达式语法,但需要使用反斜杠进行转义。
    export const config = {
         
    matcher: ['/about', '/dashboard/:path*', '/users/:uid'],
    };
    

unstable_ignorePaths

这个配置项可以让中间件忽略某些路径,即不对其应用中间件逻辑。这对于排除不需要处理的资源或者避免循环重定向等问题非常有用。

export const config = {
   
  unstable_ignorePaths: ['/api/*', '/static/*'],
};

这里,所有以 /api /开头和静态资源路径都将被忽略,不会受到中间件的影响。

maxDuration

maxDuration 定义了中间件函数的最大执行时间(以秒为单位)。如果中间件执行时间超过了这个值,Next.js将会抛出错误。这有助于防止长时间运行的任务阻塞请求处理。

export const config = {
   
  maxDuration: 5, // 中间件最大执行时间为5秒
};

regions

regions 配置项指定了中间件应该部署到的地理区域。这对于希望减少延迟、提高性能的应用来说非常重要,因为它可以让中间件尽可能靠近用户部署。

export const config = {
   
  regions: ['us-east-1', 'eu-west-1'], // 在美国东部和欧洲西部部署
};

下面是一个结合了多个config配置项的例子:

// middleware.js
import {
    NextResponse } from 'next/server';

export function middleware(request) {
   
  // 中间件逻辑...
  return NextResponse.next();
}

export const config = {
   
  matcher: ['/about', '/dashboard/:path*', '/users/:uid'],
  unstable_ignorePaths: ['/api/*', '/static/*'],
  maxDuration: 10,
  regions: ['us-east-1', 'eu-west-1'],
};

总结

总之,Next.js中间件不仅仅是一个简单的请求处理器,它更像是一个构建模块,允许开发者以一种非侵入式的方式对HTTP请求进行预处理和后处理。这不仅简化了代码逻辑,提高了代码的可维护性,而且还有助于创建更加快速、安全和用户友好的Web体验。无论你是刚开始接触Next.js的新手,还是已经熟悉框架的老手,掌握中间件的使用都将为你的项目带来显著的价值。

Githubnext-admin

线上预览地址Next Admin

相关文章
|
1月前
|
存储 安全 API
Next.js 实战 (九):使用 next-auth 完成第三方身份登录验证
这篇文章介绍了next-auth,一个为Next.js设计的身份验证库,支持多种认证方式,如电子邮件和密码、OAuth2.0提供商(如Google、GitHub、Facebook等)以及自定义提供商。文章包含了如何配置Github Provider以及会话管理,并提到了适配器Adapters在next-auth中的作用。最后,文章强调了next-auth的强大功能值得进一步探索。
77 10
|
1月前
|
设计模式 数据安全/隐私保护
Next.js 实战 (七):浅谈 Layout 布局的嵌套设计模式
这篇文章介绍了在Next.js框架下,如何处理中后台管理系统中特殊页面(如登录页)不包裹根布局(RootLayout)的问题。作者指出Next.js的设计理念是通过布局的嵌套来创建复杂的页面结构,这虽然保持了代码的整洁和可维护性,但对于特殊页面来说,却造成了不必要的布局包裹。文章提出了一个解决方案,即通过判断页面的skipGlobalLayout属性来决定是否包含RootLayout,从而实现特殊页面不包裹根布局的目标。
93 33
|
1月前
|
缓存 NoSQL JavaScript
Vue.js应用结合Redis数据库:实践与优化
将Vue.js应用与Redis结合,可以实现高效的数据管理和快速响应的用户体验。通过合理的实践步骤和优化策略,可以充分发挥两者的优势,提高应用的性能和可靠性。希望本文能为您在实际开发中提供有价值的参考。
56 11
|
1月前
|
敏捷开发 人工智能 JavaScript
Figma-Low-Code:快速将Figma设计转换为Vue.js应用,支持低代码渲染、数据绑定
Figma-Low-Code 是一个开源项目,能够直接将 Figma 设计转换为 Vue.js 应用程序,减少设计师与开发者之间的交接时间,支持低代码渲染和数据绑定。
111 3
Figma-Low-Code:快速将Figma设计转换为Vue.js应用,支持低代码渲染、数据绑定
|
1月前
|
中间件 API
Next.js 实战 (八):使用 Lodash 打包构建产生的“坑”?
这篇文章介绍了作者在使用Nextjs15进行项目开发时遇到的部署问题。在部署过程中,作者遇到了打包构建时的一系列报错,报错内容涉及动态代码评估在Edge运行时不被允许等问题。经过一天的尝试和调整,作者最终删除了lodash-es库,并将radash的部分源码复制到本地,解决了打包报错的问题。文章最后提供了项目的线上预览地址,并欢迎读者留言讨论更好的解决方案。
41 10
|
1月前
|
JavaScript 前端开发
【Vue.js】监听器功能(EventListener)的实际应用【合集】
而此次问题的核心就在于,Vue实例化的时机过早,在其所依赖的DOM结构尚未完整构建完成时就已启动挂载流程,从而导致无法找到对应的DOM元素,最终致使计算器功能出现异常,输出框错误地显示“{{current}}”,并且按钮的交互功能也完全丧失响应。为了让代码结构更为清晰,便于后续的维护与管理工作,我打算把HTML文件中标签内的JavaScript代码迁移到外部的JS文件里,随后在HTML文件中对其进行引用。
53 8
|
3月前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
50 1
JavaScript中的原型 保姆级文章一文搞懂
|
7月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
138 2
|
3月前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
35 0
|
7月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
197 4

热门文章

最新文章