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

Django+React电商网站JWT授权:前端Fetch获取Token报错求助

问题场景

我正在用Django、Django REST API和React搭建电商网站,采用Rest Framework Simple JWT实现授权功能,自定义了MyTokenObtainPairView用于获取携带用户数据的Token。后端配置、视图、URL均正常,但前端登录时通过Fetch请求获取Token失败,报错信息:TypeError: Failed to execute 'fetch' on 'Window': Invalid name。

相关代码如下:

后端代码

Settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}


SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=90),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': False,

    'ALGORITHM': 'HS256',
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'JWK_URL': None,
    'LEEWAY': 0,

    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
    'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',

    'JTI_CLAIM': 'jti',

    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}

Views.py

from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)

        # Add custom claims
        token['username'] = user.username
        # ...

        return token

class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

@api_view(['GET','POST'])
def getRoutes(request):
    routes = [
        '/api/token',
        '/api/refresh',
    ]
    return Response(routes)

Urls.py

from . import views
from .views import *
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('',views.getRoutes,name='get_routes'),
    path('token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

前端代码

React逻辑

const [username,setUsername] = useState('')
const [password,setPassword] = useState('')

const loginUser = async (e)=>{
        e.preventDefault()
        try {
          let response = await fetch('http://127.0.0.1:8000/api/token/',{
            method:'POST',
            headers:{
              'Content-Type:':'application/json',
            },
            body:JSON.stringify({'username':e.target.username.value,'password':e.target.password.value})
// tried to use useState variables to hold the value & send it through body of fetch!
          })
          const data = await response.json()
          setAuthTokens(data)

        }  catch (error) {
          console.log("failed:",error)
        }
      }

登录表单

<form className="form" onSubmit={loginUser}>
                <h5>Username:</h5>
                <input type="text" value={username} name='username' onChange={(e)=>setUsername(e.target.value)} />
                <h5>Password:</h5>
                <input type="password" value={password} name='password' onChange={(e)=>setPassword(e.target.value)} />
                <button className="login-btn" type='submit'>Sign-in</button>
            <p className="terms">By continuing, you agree to Amazon's Conditions of Use and Privacy Notice.</p>
            <div className="new-acc">
                <p className='new'>New to Amazon?</p>
                <hr />
                <br />
                <button className="create-btn">Create Your Amazon Account</button>
            </div>
            </form>

错误原因分析
  1. Header键名错误:报错Invalid name的直接原因是Fetch请求的headers中Content-Type键名多了一个结尾冒号(写成了'Content-Type:'),浏览器无法识别非法的Header字段名,从而抛出错误。
  2. 冗余的DOM依赖:已经通过useState维护了username和password状态,却仍使用e.target.username.value获取值,既冗余又容易引发DOM引用问题。
  3. 潜在跨域问题:React默认端口3000与Django默认端口8000属于跨域请求,即使修复Header问题,后续可能出现CORS拦截错误。

修复步骤

1. 修正Header键名

将'Content-Type:'改为合法的'Content-Type':

headers:{
  'Content-Type':'application/json',
},

2. 使用状态变量传递请求体

直接用已维护的状态变量替代DOM元素取值,代码更简洁可靠:

body:JSON.stringify({'username': username, 'password': password})

修正后的完整loginUser函数:

const loginUser = async (e)=>{
    e.preventDefault()
    try {
      let response = await fetch('http://127.0.0.1:8000/api/token/',{
        method:'POST',
        headers:{
          'Content-Type':'application/json',
        },
        body:JSON.stringify({'username': username, 'password': password})
      })
      const data = await response.json()
      setAuthTokens(data)

    }  catch (error) {
      console.log("failed:",error)
    }
}

3. 解决跨域问题(必做)

在Django中安装并配置django-cors-headers:

  • 安装依赖:pip install django-cors-headers
  • 修改settings.py配置:
INSTALLED_APPS = [
    # ... 其他已安装应用
    'corsheaders',
]

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

# 开发环境允许所有跨域请求(生产环境建议指定具体域名)
CORS_ALLOW_ALL_ORIGINS = True

验证修复

重启Django服务与React开发服务器,重新提交登录表单,此时应能正常获取JWT Token并存储到authTokens状态中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 17:27:26