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

NextJS服务端组件GET请求未携带Django会话Cookie问题排查

问题:Next.js服务端组件请求Django后端无法携带Cookie导致用户未识别

我用Next.js做前端、Django做后端开发时碰到一个问题:服务端组件发送获取Post数据的GET请求时,不会携带Cookie,导致Django后端拿不到sessionid,request.user显示为AnonymousUser;但客户端组件触发的登出GET请求却能正常带Cookie,后端也能识别用户。

我已经用django-cors-headers处理跨域,试过设置Django的SESSION_COOKIE_SAMESITE=None,但问题还是没解决。


核心原因

Next.js的服务端组件(Server Component)运行在Node.js服务器环境,和浏览器客户端完全隔离——浏览器存储的Cookie(如sessionid)无法被服务端组件直接访问,因此fetch请求自然无法携带这些Cookie。而客户端组件在浏览器中执行,能直接读取浏览器的Cookie存储,所以请求可以正常携带Cookie。

另外你提供的getPostByID代码里有个笔误:重复定义了response(写成了respnse),这会导致后续逻辑出错,建议修正。


修复方案

方案1:将请求逻辑移至客户端组件

如果获取Post数据不需要服务端预渲染,直接把请求逻辑放到客户端组件中(比如用useEffect触发,或直接在组件内调用),这样请求在浏览器端执行,就能自动携带Cookie:

// 客户端组件示例
'use client';

import { useEffect, useState } from 'react';

export default function PostClientComponent({ id }: { id: string }) {
  const [postData, setPostData] = useState(null);

  useEffect(() => {
    const fetchPost = async () => {
      const apiToken = await getCSRFToken();
      const response = await fetch(`http://localhost:8000/posts/?id=${id}`, {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          "X-CSRFToken": apiToken,
        },
        credentials: "include",
      });
      const data = await response.json();
      setPostData(data);
    };
    fetchPost();
  }, [id]);

  return <div>{/* 渲染postData */}</div>;
}

方案2:服务端组件手动传递Cookie

如果必须在服务端组件中请求数据,需要从Next.js的请求对象中获取浏览器发送的Cookie,然后手动添加到fetch请求头中:

// 服务端组件中获取Cookie并传递
import { cookies } from 'next/headers';

export async function getPostByID(id: string, cookieString: string) {
  const apiToken = await getCSRFToken();
  const response = await fetch(`http://localhost:8000/posts/?id=${id}`, {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
      "X-CSRFToken": apiToken,
      "Cookie": cookieString, // 手动携带Cookie
    },
    credentials: "include",
  });
  const data = await response.json();
  return data;
}

// 服务端组件页面
export default async function PostPage({ params }: { params: { id: string } }) {
  const cookieStore = cookies();
  const cookieString = cookieStore.toString(); // 转换为Cookie字符串格式
  const postData = await getPostByID(params.id, cookieString);
  return <div>{/* 渲染postData */}</div>;
}

方案3:确保Django跨域Cookie配置正确

检查并修正Django的settings.py相关配置,确保Cookie能正常跨域传递:

# settings.py
INSTALLED_APPS = [
    # ... 其他应用
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    # ... 其他中间件
]

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",  # Next.js本地开发地址
]

SESSION_COOKIE_SAMESITE = 'Lax'  # 跨域场景下可设为None,注意需要配合SESSION_COOKIE_SECURE=True(生产环境HTTPS)
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = False  # 本地开发用False,生产环境必须设为True

相关代码参考

获取Post数据的前端代码(原代码)

export async function getPostByID(id: string) {
    const apiToken = await getCSRFToken();
    const response = await fetch(
      "http://localhost:8000/posts/?id=" + id,
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          "X-CSRFToken": apiToken,
        },
        credentials: "include",
      }
    );
    // 注意:此处笔误,respnse应为response
    const respnse = await fetch(
      "http://localhost:8000/posts/comments",
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          "X-CSRFToken": apiToken,
        },
        credentials: "include",
      }
    );
    const data = await response.json();
    return data;
}

Django PostsView视图代码(原代码)

class PostsView(View):
    def get(self, request):
        id = request.GET.get('id')
        if id is not None:
            post = get_object_or_404(Post ,id=id)
            comments = []
            for comment in post.comments.all():
                comments.append({
                    'id': comment.id,
                    'author': comment.author.username,
                    'post': comment.post.id,
                    'content': comment.content,
                    'created_at': comment.created_at,
                    'updated_at': comment.updated_at
                })
            try:
                Like.objects.get(author=request.user.id, post=post.id)
                liked = True
            except Like.DoesNotExist:
                liked = False
            return JsonResponse({'id': post.id, 'author':{'id': post.author.id, 'email': post.author.email, 'username': post.author.username}, 'title': post.title, 'content': post.content, 'comments': comments, 'created_at': post.created_at, 'updated_at': post.updated_at, 'likes': post.getLikesCount(), 'liked': liked}, status=200)
        
        userID = request.GET.get('user')
        if userID is not None:
            user = get_object_or_404(User, id=userID)
            posts = get_list_or_404(Post, author=user)
            return JsonResponse({'posts': posts}, safe=False, status=200)
        
        posts = Post.objects.all()
        response = {'posts': []}
        for post in posts:
            response['posts'].append({'id': post.id, 'author':{'id': post.author.id, 'email': post.author.email, 'username': post.author.username}, 'title': post.title, 'content': post.content, 'created': post.created_at, 'updated': post.updated_at, 'likes': post.getLikesCount()})
        return JsonResponse(response, status=200)

登出请求前端代码(原代码)

async function handleLogout() {
    const apiToken = await getCSRFToken();
    const reponse = await fetch("http://localhost:8000/accounts/logout/", {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        "X-CSRFToken": apiToken,
      } as any,
      credentials: "include",
    });
    const data = await reponse.json();
    if (!reponse.ok) {
      toast.error(data.message, {
        position: "top-right",
        autoClose: 5000,
        hideProgressBar: false,
        closeOnClick: false,
        pauseOnHover: false,
        draggable: false,
        progress: undefined,
        theme: "dark",
        transition: Flip,
      });
      return;
    } else {
      toast.success(data.message, {
        position: "top-right",
        autoClose: 5000,
        hideProgressBar: false,
        closeOnClick: false,
        pauseOnHover: false,
        draggable: false,
        progress: undefined,
        theme: "dark",
        transition: Flip,
      });
      context.setUser(undefined);
    }
  }

Django LogoutView视图代码(原代码)

class LogoutView(View):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return JsonResponse({'message': 'Not logged in!'}, status=401)
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        logout(request)
        return JsonResponse({'message': 'Successfully logged out.'}, status=200)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:54:53