Next.js куки не сохраняются в браузере

Вопрос или проблема

У меня проблема с сохранением файлов 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 будет правильно сохраняться в кукис и использовано при переходах. Однако, при обновлении страницы с другим языком, куки не обновляется, и вместо текущей локали, используется та, что была сохранена ранее.

Возможные причины и решения

  1. Проблема с обновлением кукис:
    Внутри функции updateSession вы используете метод set() для установки кукис, однако в текущей реализации он не применяется корректно. Попробуйте сделать следующее:

    setAll(cookiesToSet) {
       cookiesToSet.forEach(({ name, value, options }) => {
           request.cookies.set(name, value, options); // Это не сохраняет куки
           supabaseResponse.cookies.set(name, value, options); // Это нужно для сохранения
       });
    }

    Убедитесь, что вы сохраняете куки с правильными параметрами, такими как httpOnly и sameSite.

  2. Проблема с повторным определением локали:
    В функции languagesMiddleware, вы устанавливаете кукис, но не сохраняете их, если локаль уже определена. Например:

    if (pathnameHasLocale) {
       const locale = pathname.split("/")[1];
       request.cookies.set(cookieName, locale); // Можно добавить check и установить только если не совпадает
       return NextResponse.next({ request });
    }
  3. Отсутствие правильного редиректа:
    При использовании редиректа в функции languagesMiddleware, вы должны убедиться, что новый URL корректно содержит куки с актуальной локалью. Например, вы можете проверить, сохранены ли куки до редиректа.

  4. Отладка куки:
    Используйте инструменты разработчика (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. Надеюсь, эти рекомендации помогут вам решить вашу проблему!

Оцените материал
Добавить комментарий

Капча загружается...