如何在 Next.js (Pages Router) v16 中验证 URL 中的语言环境参数

优雅地处理不支持的语言环境代码

问题

当区域代码成为 URL 结构的一部分时,它们会转变为必须验证的用户输入。访问者可以轻松输入 /xx/about/gibberish/contact,就像输入有效的区域代码如 /en/about 一样。如果没有验证,应用程序可能会崩溃、显示损坏的内容或显示令人困惑的错误消息。遇到无效区域代码 URL 的用户需要一个明确的解决路径——要么通过重定向到有效的区域代码,要么通过适当的 "未找到" 响应来帮助他们理解问题所在。

解决方案

在请求到达页面组件之前,根据支持的区域代码列表验证传入的区域参数。使用 Next.js 中间件拦截请求,检查 URL 中的区域代码是否与支持的值匹配,并做出适当的响应。对于无效的区域代码,将用户重定向到默认区域代码或重写请求以显示 404 页面。这确保了只有有效的区域代码才能渲染内容,而无效的代码会被优雅地处理,不会破坏用户体验。

步骤

1. 在 Next.js 配置中定义支持的区域代码

next.config.js 中配置 i18n 设置,以声明您的应用程序支持哪些区域代码以及哪个区域代码作为默认值。

module.exports = {
  i18n: {
    locales: ["en", "fr", "de"],
    defaultLocale: "en",
    localeDetection: false,
  },
};

localeDetection 设置为 false 可以防止基于浏览器偏好的自动重定向,从而让您完全控制区域代码的处理。

2. 创建中间件以验证区域参数

在项目根目录或 src 文件夹(如果使用的话)中创建一个 middleware.ts 文件。

import { NextRequest, NextResponse } from "next/server";

const SUPPORTED_LOCALES = ["en", "fr", "de"];
const DEFAULT_LOCALE = "en";

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  const pathnameHasLocale = SUPPORTED_LOCALES.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );

  if (!pathnameHasLocale) {
    return;
  }

  const localeInPath = pathname.split("/")[1];

  if (!SUPPORTED_LOCALES.includes(localeInPath)) {
    const url = request.nextUrl.clone();
    url.pathname = pathname.replace(`/${localeInPath}`, `/${DEFAULT_LOCALE}`);
    return NextResponse.redirect(url);
  }
}

export const config = {
  matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};

中间件从 URL 路径中提取区域代码,将其与支持的区域代码数组进行检查,并将无效的区域代码重定向到默认区域代码,同时保留路径的其余部分。

3. 使用 404 响应处理无效的语言环境

如果您希望显示 404 页面而不是重定向,请将请求重写到自定义的 404 页面。

import { NextRequest, NextResponse } from "next/server";

const SUPPORTED_LOCALES = ["en", "fr", "de"];

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  const pathnameHasLocale = SUPPORTED_LOCALES.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );

  if (!pathnameHasLocale) {
    return;
  }

  const localeInPath = pathname.split("/")[1];

  if (!SUPPORTED_LOCALES.includes(localeInPath)) {
    const url = request.nextUrl.clone();
    url.pathname = "/404";
    return NextResponse.rewrite(url);
  }
}

export const config = {
  matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};

pages/404.js 中创建一个自定义的 404 页面,该页面将在构建时静态生成。

4. 创建自定义 404 页面

export default function Custom404() {
  return (
    <div>
      <h1>404 - 页面未找到</h1>
      <p>您正在查找的页面不存在。</p>
    </div>
  );
}

当中间件重写无效的语言环境请求时,此页面会显示,为用户提供一个清晰的信息,表明请求的 URL 不可用。