Возникновение проблемы 302 редиректа при отправке формы в приложении React/Django, поскольку вызов API перенаправляется
В моем приложении Django/React я хочу, чтобы пользователь был зарегистрирован для успешной отправки запроса к API.
Я успешно вошел в систему с помощью google и считаю, что передаю все правильные данные в backend api для его вызова.
Я настроил CORS, убедился, что мои CSRF-токены совпадают, использую @login_required в представлении Django, использую "credentials: 'include'" в api вызове, и исчерпал как ChatGPT, так и свой собственный поиск в интернете, чтобы решить проблему.
Из моего файла Conversation.jsx я хочу вызвать /api/ask_new_york из app/views.py. Даже при входе в систему я получаю ответ 302.
Вот весь, как мне кажется, релевантный код и вывод:
settings.py:
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'allauth.account.middleware.AccountMiddleware',
]
# CORS settings
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
# CSRF settings
CSRF_TRUSTED_ORIGINS = [
'http://localhost:3000',
'http://localhost:8000',
'http://127.0.0.1:8000',
'http://localhost'
]
# Disable CSRF cookies only if needed
CSRF_COOKIE_SECURE = False
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/'
LOGOUT_REDIRECT_URL = '/'
app/views.py:
@csrf_exempt
def get_csrf_token(request):
csrf_token = get_token(request)
return JsonResponse({'csrf_token': csrf_token})
@login_required
def ask_new_york(request):
if request.method == 'POST':
try:
data = json.loads(request.body.decode('utf-8'))
input_value = data.get('input_value'
users/views.py: Здесь происходит создание пользователей (это весь файл)
from django.contrib.auth import get_user_model
from django.conf import settings
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from google.oauth2 import id_token
from google.auth.transport import requests as google_requests
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .serializers import UserSerializer
from django.db import IntegrityError
User = get_user_model()
@api_view(['POST'])
@csrf_exempt
def google_login(request):
if request.method == 'POST':
try:
token = request.data.get('token')
if not token:
return JsonResponse({'error': 'No token provided'}, status=400)
# Verify the token using Google's API
idinfo = id_token.verify_oauth2_token(token, google_requests.Request(), settings.GOOGLE_CLIENT_ID)
# If token is valid, get the user info
email = idinfo['email']
name = idinfo.get('name', '')
# Check if the user exists, if not, create a new user
user, created = User.objects.get_or_create(email=email, defaults={'username': email, 'first_name': name})
if created:
user.set_unusable_password() # If user is created, ensure no password is set
user.save()
# Return user info
return JsonResponse({'email': user.email, 'name': user.first_name})
except ValueError:
return JsonResponse({'error': 'Invalid token'}, status=400)
except IntegrityError:
# Handle the case where the email already exists
return JsonResponse({'error': 'User with this email already exists'}, status=400)
return JsonResponse({'error': 'Invalid request method'}, status=400)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def user_detail(request):
user = request.user
serializer = UserSerializer(user)
return Response(serializer.data)
LoginSignUpPage.jsx: Фронтенд-страница, на которой пользователь фактически регистрируется. (это весь файл)
import React, { useContext, useEffect, useState } from 'react';
import { GoogleLogin } from '@react-oauth/google';
import { useNavigate } from 'react-router-dom';
import { jwtDecode } from "jwt-decode";
import AuthContext from './AuthContext';
import axios from 'axios';
import { getCsrfToken } from './csrfToken'; // Import the utility
const LoginSignupPage = () => {
const { setAuthUser } = useContext(AuthContext);
const navigate = useNavigate();
const [csrfToken, setCsrfToken] = useState('');
useEffect(() => {
const token = getCsrfToken(); // Use the centralized function to get CSRF token
setCsrfToken(token);
console.log("CSRF token set as:", token);
}, []);
const handleLoginSuccess = async (response) => {
try {
const decoded = jwtDecode(response.credential); // Decode JWT
const { email, name } = decoded;
// Send token to backend for verification and authentication
const res = await axios.post(
'http://localhost:8000/api/google-login/',
{ token: response.credential },
{
headers: {
'X-CSRFToken': csrfToken, // Include the CSRF token in the headers
},
}
);
const user = {
email: res.data.email,
name: res.data.name,
created_at: res.data.created_at,
};
console.log(user); // Log user info
// Save user data in context or global state
setAuthUser(user);
// Redirect to home page after successful login
navigate('/');
} catch (error) {
console.error('Login Failed:', error);
}
};
const handleLoginFailure = (error) => {
console.log('Login Failed:', error);
};
return (
<div>
<h1>Login</h1>
<GoogleLogin onSuccess={handleLoginSuccess} onError={handleLoginFailure} />
</div>
);
};
export default LoginSignupPage;
Conversation.jsx: Домашняя страница, на которой пользователь отправляет текст в API, обрабатываемый в handleSubmit. Здесь также есть обработка ошибок и компоненты фронт-энда, которые выполняют handleSubmit при нажатии.
import { getCsrfToken } from './csrfToken';
useEffect(() => {
const token = getCsrfToken(); // Use the centralized function to get CSRF token
setCsrfToken(token);
console.log("CSRF token set as:", token);
}, []);
const handleSubmit = async (e) => {
try {
let headers = {
'X-CSRFToken': csrfToken,
'Content-Type': 'application/json',
};
response = await fetch("http://localhost:8000/api/ask_new_york/", {
method: "POST", // Specify POST method
headers: headers,
credentials: 'include', // Ensures cookies are sent with the request
body: JSON.stringify({ input_value: inputValue }),
});
}
csrfToken.js: Получает csrftoken (это весь файл)
export const getCookie = (name) => {
const value = `; ${document.cookie}`;
console.log(value)
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
};
export const getCsrfToken = () => {
return getCookie('csrftoken'); // Adjust the name if your cookie is named differently
};
В моей веб-консоли файлы csrfToken.js, LoginSignUpPage.jsx и Conversation.jsx регистрируют csrftoken, который у них есть, и все они одинаковы. При входе в систему и выполнении handleSubmit в Conversation.jsx я получаю следующие ошибки: В Google Chrome:
Error submitting data: SyntaxError: Unexpected token '<', "<!doctype "... is not valid JSON
Я полагаю, что он получает html страницы /login, на которую хочет перенаправить пользователя, даже если он вошел в систему. Возможно, здесь я ошибаюсь.
В моем внутреннем терминале:
[11/Sep/2024 21:35:31] "POST /api/ask_new_york/ HTTP/1.1" 302 0
[11/Sep/2024 21:35:31] "GET /login/?next=/api/ask_new_york/ HTTP/1.1" 200 644.
Я не знаю, почему происходит ошибка 302.
Экстра:
[11/Sep/2024 21:35:22] "OPTIONS /api/google-login/ HTTP/1.1" 200 0
[11/Sep/2024 21:35:22] "POST /api/google-login/ HTTP/1.1" 200 47
Вход в систему Google работает нормально.