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

Next.js中间件无法获取fetch API返回JSON数据问题求助

问题:Next.js中间件调用/api/me接口返回未授权

我用Next.js中间件实现基于JWT的角色认证,用户登录后生成JWT令牌,通过http://localhost:3000/api/me接口获取用户数据:

  • 有令牌时返回用户ID和角色,示例:{"user":{"id":"649eaab9129b600eaccd800a","role":"admin"}}
  • 无令牌时返回未授权信息:{"message":"Unauthorized"}

浏览器访问该API能正常获取数据,但中间件里用fetch请求这个接口时,返回未授权信息。

相关代码如下:

package.json

{
  "name": "role-based-authentication-with-jwt",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@types/node": "20.4.1",
    "@types/react": "18.2.14",
    "@types/react-dom": "18.2.6",
    "autoprefixer": "10.4.14",
    "jsonwebtoken": "^9.0.1",
    "mongodb": "^5.7.0",
    "mongoose": "^7.3.2",
    "next": "13.4.9",
    "postcss": "8.4.25",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-hook-form": "^7.45.1",
    "react-toastify": "^9.1.3",
    "tailwindcss": "3.3.2",
    "typescript": "5.1.6"
  },
  "devDependencies": {
    "@types/jsonwebtoken": "^9.0.2"
  }
}

middleware.ts

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

export async function middleware(request: NextRequest) {
  const token = request.cookies.get("token")?.value;
  const response = await fetch("http://localhost:3000/api/me");
  const { user } = await response.json();
  if (!token && !user?.role) {
    if (!request.nextUrl.pathname.startsWith("/login")) {
      return NextResponse.redirect(new URL("/login", request.url));
    }
  } else if (
    token &&
    user?.role &&
    !request.nextUrl.pathname.startsWith(`/${user?.role}`) &&
    !request.nextUrl.pathname.startsWith("/login")
  ) {
    return NextResponse.redirect(new URL("/access", request.url));
  } else {
    if (!request.nextUrl.pathname.startsWith(`/${user?.role}`)) {
      return NextResponse.redirect(new URL(`/${user?.role}`, request.url));
    }
  }
}

export const config = {
  matcher: ["/admin/:path*", "/superadmin/:path*", "/user/:path*", "/login"],
};

api/me/route.ts

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

export async function GET(request: NextRequest) {
  try {
    const token = request.cookies.get("token")?.value || "";
    if (!token) {
      return NextResponse.json(
        {
          message: "Unauthorized",
        },
        {
          status: 401,
        }
      );
    }
    const user = getUser(request);
    return NextResponse.json({
      user,
    });
  } catch (error: any) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
}

helpers/getUser.ts

import { NextRequest } from "next/server";
import { verify } from "jsonwebtoken";

export const getUser = (request: NextRequest): TokenData => {
  try {
    const token = request.cookies.get("token")?.value || "";
    const decodedToken: any = verify(token, process.env.TOKEN_SECRET!);
    return { id: decodedToken.id, role: decodedToken.role };
  } catch (error: any) {
    throw new Error(error.message);
  }
};

需要解决:如何在中间件中通过fetch API获取用户角色,完成角色路由控制?


解决方案

问题核心是中间件中的fetch请求未携带用户cookie:浏览器发起请求时会自动带上当前域名的cookie,但中间件里的fetch是服务端发起的请求,默认不会携带任何cookie,导致/api/me接口拿不到token,返回未授权。

方案1:直接在中间件中解析JWT(推荐)

没必要通过fetch调用/api/me接口,直接复用getUser工具函数在中间件里解析token获取角色,减少一次HTTP请求,性能更优:

修改后的middleware.ts:

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { getUser } from "@/helpers/getUser";

export async function middleware(request: NextRequest) {
  let user = null;
  const token = request.cookies.get("token")?.value;
  
  try {
    if (token) {
      user = getUser(request);
    }
  } catch (err) {
    // 解析token失败,视为无有效用户
    user = null;
  }

  if (!token && !user?.role) {
    if (!request.nextUrl.pathname.startsWith("/login")) {
      return NextResponse.redirect(new URL("/login", request.url));
    }
  } else if (
    token &&
    user?.role &&
    !request.nextUrl.pathname.startsWith(`/${user.role}`) &&
    !request.nextUrl.pathname.startsWith("/login")
  ) {
    return NextResponse.redirect(new URL("/access", request.url));
  } else if (user?.role && !request.nextUrl.pathname.startsWith(`/${user.role}`)) {
    return NextResponse.redirect(new URL(`/${user.role}`, request.url));
  }
}

export const config = {
  matcher: ["/admin/:path*", "/superadmin/:path*", "/user/:path*", "/login"],
};

方案2:让fetch请求携带cookie

如果一定要通过调用/api/me接口获取用户信息,需要在fetch时手动传递cookie:

修改中间件中的fetch部分:

const response = await fetch("http://localhost:3000/api/me", {
  headers: {
    Cookie: request.headers.get("Cookie") || ""
  }
});

这样服务端发起的fetch请求就会携带用户的cookie,/api/me接口就能正常解析token返回用户数据。

注意:这种方式会多一次内部HTTP请求,性能不如方案1,更推荐直接解析token。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:15:15