You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js动态路由权限校验问题:非法相册路径拦截失败

问题解决:Next.js动态路由相册校验与Mongoose报错处理

核心问题分析

你遇到的Cannot read properties of undefined (reading 'User')报错,本质是Next.js Middleware默认运行在Edge Runtime环境,而Mongoose依赖Node.js核心API(如net、fs),这些在Edge环境中不可用,导致模型初始化失败。同时直接在Middleware中操作数据库校验相册的思路不符合Edge Runtime的限制。

可行解决方案

方案1:页面组件内服务端校验(推荐,App Router)

在app/albums/[albumId]/page.js中直接做服务端校验,利用Next.js App Router的服务器组件特性,无需依赖Middleware:

import { notFound, redirect } from 'next/navigation';
import { getAlbumById } from '@/lib/albums'; // 自定义相册查询函数(基于Mongoose)
import { getCurrentUser } from '@/lib/auth'; // 获取当前登录用户信息

async function AlbumDetailPage({ params }) {
  // 1. 校验用户登录状态
  const user = await getCurrentUser();
  if (!user) {
    redirect('/login');
  }

  // 2. 校验相册是否存在且属于当前用户
  const album = await getAlbumById(params.albumId, user.id);
  if (!album) {
    notFound(); // 返回404页面
  }

  // 3. 渲染相册详情
  return (
    <div>
      <h1>{album.name}</h1>
      {/* 相册内容渲染 */}
    </div>
  );
}

export default AlbumDetailPage;

对应的getAlbumById实现示例:

// lib/albums.js
import Album from '@/models/Album';

export async function getAlbumById(albumId, userId) {
  return await Album.findOne({ _id: albumId, userId });
}

方案2:API Route配合Middleware校验

如果必须用Middleware拦截请求,可通过内部API Route中转数据库操作(API Route默认运行在Node.js环境,支持Mongoose):

步骤1:创建校验API Route

app/api/validate-album/route.js:

import { NextResponse } from 'next/server';
import { getAlbumById } from '@/lib/albums';
import { getCurrentUser } from '@/lib/auth';

export async function POST(request) {
  const { albumId } = await request.json();
  const user = await getCurrentUser();

  if (!user) {
    return NextResponse.json({ valid: false }, { status: 401 });
  }

  const album = await getAlbumById(albumId, user.id);
  return NextResponse.json({ valid: !!album });
}

步骤2:Middleware中调用API校验

middleware.js:

import { NextResponse } from 'next/server';

export async function middleware(request) {
  const pathMatch = request.nextUrl.pathname.match(/^\/albums\/([^/]+)$/);
  if (!pathMatch) return NextResponse.next();

  const albumId = pathMatch[1];
  // 调用内部校验API
  const validateRes = await fetch(new URL('/api/validate-album', request.nextUrl.origin), {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ albumId }),
    credentials: 'include', // 携带用户认证Cookie
  });

  const { valid } = await validateRes.json();
  if (!valid) {
    // 校验失败,重定向到相册列表或返回404
    return NextResponse.redirect(new URL('/albums', request.nextUrl.origin));
  }

  return NextResponse.next();
}

export const config = {
  matcher: '/albums/:path*',
};

方案3:使用MongoDB Edge SDK直接在Middleware操作

如果不想依赖API Route,可改用MongoDB官方Edge SDK(兼容Edge Runtime)直接查询:

// middleware.js
import { MongoClient } from 'mongodb';
import { NextResponse } from 'next/server';
import { getUserIdFromRequest } from '@/lib/auth'; // 从请求中提取用户ID(如Cookie/JWT)

const client = new MongoClient(process.env.MONGODB_URI);

export async function middleware(request) {
  const pathMatch = request.nextUrl.pathname.match(/^\/albums\/([^/]+)$/);
  if (!pathMatch) return NextResponse.next();

  const albumId = pathMatch[1];
  const userId = getUserIdFromRequest(request);
  if (!userId) return NextResponse.redirect(new URL('/login', request.nextUrl.origin));

  // 连接数据库并校验相册
  await client.connect();
  const db = client.db();
  const album = await db.collection('albums').findOne({ _id: albumId, userId });

  if (!album) {
    return NextResponse.redirect(new URL('/albums', request.nextUrl.origin));
  }

  return NextResponse.next();
}

内容的提问来源于stack exchange,提问作者gj_dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 13:17:32