Вопрос или проблема
У меня проблема с сохранением файлов cookie.
Я использую два промежуточных модуля, которые я связываю, чтобы: перенаправить на правильный язык и сохранить язык как файл cookie / затем получить сессию пользователя с помощью клиента Supabase.
Что я ожидал:
1/ Если пользователь переходит по URL, содержащему язык, мы сохраняем этот язык в файлах cookie и переходим непосредственно к следующему промежуточному модулю, обрабатывающему сессию. Если он переходит на другую страницу, язык должен оставаться тем же.
2/ Если пользователь переходит по URL, не содержащему язык, мы перенаправляем пользователя на тот же URL, предварительно добавив язык (сохранённый в файлах cookie, если он сохранён) или “en”. Это должно вернуть пользователя к пункту 1/.
Фактическое поведение с проблемой: Второй вариант. Если я сбрасываю кэш, и “fr” изначально сохранён в файлах cookie, он никогда не обновляется. Если пользователь обновляет страницу с “en” как языком, а затем кликает на ссылку, его перенаправляет на URL с “fr” в качестве языка.
export async function updateSession(request) {
let supabaseResponse = NextResponse.next({
request,
});
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value }) =>
request.cookies.set(name, value),
);
supabaseResponse = NextResponse.next({
request,
});
cookiesToSet.forEach(({ name, value, options }) =>
supabaseResponse.cookies.set(name, value, options),
);
},
},
},
);
const {
data: { user },
} = await supabase.auth.getUser();
return supabaseResponse;
}
function getLocale(request) {
// Проверяет, есть ли язык, уже сохранённый в файле cookie
if (request.cookies.has(cookieName)) {
return request.cookies.get(cookieName).value;
}
const acceptLang = request.headers.get("Accept-Language");
if (!acceptLang) return defaultLocale;
const headers = { "accept-language": acceptLang };
const languages = new Negotiator({ headers }).languages();
return match(languages, locales, defaultLocale);
}
export function languagesMiddleware(request) {
const { pathname } = request.nextUrl;
const isApiRoute = pathname.startsStartsWith("/api/") || pathname === "/api";
if (isApiRoute) return NextResponse.next({ request });
const pathnameHasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
);
if (pathnameHasLocale) {
request.cookies.set(cookieName, pathname.split("/")[1]);
return NextResponse.next({ request });
}
const locale = getLocale(request);
request.nextUrl.pathname = `/${locale}${pathname}`;
const newResponse = NextResponse.redirect(request.nextUrl);
newResponse.headers.set("X-Redirect-Path", request.nextUrl.pathname);
return newResponse;
}
export async function middleware(request) {
const languageResponse = languagesMiddleware(request);
if (languageResponse?.headers?.get("X-Redirect-Path"))
return languageResponse;
return await updateSession(request);
}
Похоже, что файлы cookie сохраняются правильно, поскольку в конце функции updateSession, когда я использую console.log supabaseResponse, я получаю:
{
cookies: ResponseCookies {},
url: '',
body: null,
bodyUsed: false,
headers: {
x-middleware-next: '1',
x-middleware-override-headers: 'accept,accept-encoding,accept-language,connection,cookie,host,sec-ch-ua,sec-ch-ua-mobile,sec-ch-ua-platform,sec-fetch-dest,sec-fetch-mode,sec-fetch-site,sec-fetch-user,upgrade-insecure-requests,user-agent,x-forwarded-for,x-forwarded-host,x-forwarded-port,x-forwarded-proto',
x-middleware-request-accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
x-middleware-request-accept-encoding: 'gzip, deflate, br, zstd',
x-middleware-request-accept-language: 'en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-GB;q=0.6',
x-middleware-request-connection: 'keep-alive',
x-middleware-request-cookie: 'locale=en',
x-middleware-request-host: 'localhost:3002',
x-middleware-request-sec-ch-ua: '"Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
x-middleware-request-sec-ch-ua-mobile: '?0',
x-middleware-request-sec-ch-ua-platform: '"macOS"',
x-middleware-request-sec-fetch-dest: 'document',
x-middleware-request-sec-fetch-mode: 'navigate',
x-middleware-request-sec-fetch-site: 'none',
x-middleware-request-sec-fetch-user: '?1',
x-middleware-request-upgrade-insecure-requests: '1',
x-middleware-request-user-agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
x-middleware-request-x-forwarded-for: '::1',
x-middleware-request-x-forwarded-host: 'localhost:3002',
x-middleware-request-x-forwarded-port: '3002',
x-middleware-request-x-forwarded-proto: 'http'
},
ok: true,
redirected: false,
status: 200,
statusText: '',
type: 'default'
}
Ответ или решение
Проблема сохранения кукис в приложении на Next.js может возникать по нескольким причинам. В вашем случае, исходя из описания и приведенного кода, я вижу несколько возможных мест, где может возникать проблема. Давайте рассмотрим их и предложим решения.
Описание проблемы
Вы ожидаете, что locale будет правильно сохраняться в кукис и использовано при переходах. Однако, при обновлении страницы с другим языком, куки не обновляется, и вместо текущей локали, используется та, что была сохранена ранее.
Возможные причины и решения
-
Проблема с обновлением кукис:
Внутри функцииupdateSession
вы используете методset()
для установки кукис, однако в текущей реализации он не применяется корректно. Попробуйте сделать следующее:setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value, options }) => { request.cookies.set(name, value, options); // Это не сохраняет куки supabaseResponse.cookies.set(name, value, options); // Это нужно для сохранения }); }
Убедитесь, что вы сохраняете куки с правильными параметрами, такими как
httpOnly
иsameSite
. -
Проблема с повторным определением локали:
В функцииlanguagesMiddleware
, вы устанавливаете кукис, но не сохраняете их, если локаль уже определена. Например:if (pathnameHasLocale) { const locale = pathname.split("/")[1]; request.cookies.set(cookieName, locale); // Можно добавить check и установить только если не совпадает return NextResponse.next({ request }); }
-
Отсутствие правильного редиректа:
При использовании редиректа в функцииlanguagesMiddleware
, вы должны убедиться, что новый URL корректно содержит куки с актуальной локалью. Например, вы можете проверить, сохранены ли куки до редиректа. -
Отладка куки:
Используйте инструменты разработчика (DevTools) в браузере для проверки кукис во вкладке "Application" > "Cookies". Убедитесь, что куки сохраняются и доступны на нужных страницах. Если они не сохраняются, убедитесь, что вы не столкнулись с проблемами, связанными с безопасностью, такими как SameSite или HttpOnly.
Рекомендуемый пересмотр кода
Вот пример того, как можно изменить ваш код, чтобы улучшить处理 сохранения кукис:
export function languagesMiddleware(request) {
const { pathname } = request.nextUrl;
const isApiRoute = pathname.startsWith("/api/") || pathname === "/api";
if (isApiRoute) return NextResponse.next({ request });
const pathnameHasLocale = locales.some(locale =>
pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
);
if (pathnameHasLocale) {
const locale = pathname.split("/")[1];
// Сохранение только если локаль изменилась
if (request.cookies.get(cookieName).value !== locale) {
request.cookies.set(cookieName, locale);
}
return NextResponse.next({ request });
}
const locale = getLocale(request);
if (locale !== defaultLocale) { // Проверить, если текущее значение отличается от локали по умолчанию
request.nextUrl.pathname = `/${locale}${pathname}`;
const response = NextResponse.redirect(request.nextUrl);
response.cookies.set(cookieName, locale); // Установка куки перед редиректом
return response;
}
return NextResponse.next();
}
Заключение
Убедитесь, что вы сохраняете куки с актуальными значениями и проверяете, изменилось ли значение локали перед его установкой. Также важно проверить, правильно ли обрабатываются куки в браузере и доступны ли они вашим middleware. Надеюсь, эти рекомендации помогут вам решить вашу проблему!