Отправка GET-запроса от внешнего приложения к внутреннему не содержит cookies ( front nextJS + back Django )
В принципе, из того, что я читал в интернете, куки Django хранятся в сессиях, я полагаю, и когда мы пытаемся получить к ним доступ, то в куках будет sessionid, и это делает куки (например, доступ к пользователю, хранящемуся в запросе и прочее) достижимыми. Моя проблема заключается в том, что у меня есть front-end, и в нем я пытаюсь получить данные 'Post'. Проблема в том, что в моем GET-запросе, как я понял, нет кукисов, отправленных на бэкэнд, и из-за этого sessionid пропал, а мой пользователь, который хранится (или лучше сказать, должен храниться) в запросе, пропал (AnonymousUser это). Забавный факт: в моем Django приложении есть еще одно представление для выхода из системы, которое работает идеально, и в запросе сохраняется sessionid и доступ к пользователю. Мой вопрос в том, является ли это проблемой моих представлений ?! Является ли это общим ?! Что заставляет это происходить?! Мне очень нужно, чтобы это было исправлено, потому что весь мой проект фактически рушится из-за этой проблемы, которую я имею с получением данных. Как мне отправить запрос на получение данных '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",
}
);
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;
}
Как мне обработать запрос на получение данных 'Post' (Поскольку некоторые объекты не были JSON Serializable, мне пришлось создавать словарь самостоятельно):
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)
А вот так я отправляю запрос на выход из системы:
front-end:
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);
}
}
Задний конец:
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)
Единственная разница в том, что выход из системы осуществляется через кнопку и в клиентском компоненте, которым является navbar, а получение данных для 'Post' осуществляется в серверном компоненте, а затем полученные данные передаются в другой компонент для их отображения. Мои Django, Python, NextJS, React и все остальное - самые последние версии. Также я должен сказать, что я знаю, что лучше сделать back-end, используя Django-rest framework, но это был быстрый и небольшой проект, поэтому я просто хотел использовать сам Django вместо этого.
Я изменил настройки Django, добавив что-то вроде "COOKIE_SESSION_SAMESITE = None", и это не сработало, попытался отправить запрос другими способами с изменениями, и это не сработало, и много других вещей, которые я на самом деле не помню, потому что я пытаюсь разобраться с этим уже несколько дней. Также я использовал библиотеку django-cors-headers, так что проблем с CORS здесь нет.
Серверные компоненты в Next
(или React
, если на то пошло) не имеют автоматического доступа к cookies (фактически они имеют лишь частичный доступ к ним, и то только в том случае, если вы явно их вызываете). Это происходит потому, что cookie хранится в браузере пользователя, который, очевидно, очень отделен от сервера.
Причина, по которой выход из системы работает, заключается в том, что клиентский компонент в Next
работает в браузере пользователя, а не на сервере, что означает, что он имеет прямой доступ к cookies пользователя.
Вы можете заставить это работать, если действительно хотите, получая cookie в своем серверном компоненте и добавляя его в заголовки запроса... но я бы, пожалуй, посоветовал вместо этого делать запрос из клиентского компонента, если это вообще возможно, это избавит вас от головной боли.
Если вы действительно хотите попробовать подход "cookie-from-server":
- Я предполагаю, что вы уже позаботились о настройке Django для использования именованного cookie/поля.
- Я также предполагаю, что поскольку вы используете
django-cors-headers
, вы также настроили все необходимые параметры Django для CORS. - Любой компонент, в котором вы вызовете эту функцию, станет некэшируемым, так как cookies (неизбежно) динамичны с точки зрения сервера.
Я не знаю на 100% синтаксис fetch
, так как прошло много времени, но чтобы получить cookie и добавить его в заголовок запроса, я думаю, нужно сделать так:
/* Assumes you are using App Router */
import { cookies } from 'next/headers'
export async function getPostByID(id: string) {
const cookies = cookies()
const sessionCookie = cookies.get('sessionid')
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: "sessionid=${sessionCookie};"
},
credentials: "include",
}
);