Teilen von Cookies zwischen Websites auf derselben Domain – Headless / Entkoppeltes CMS

Lesezeit: 6 Minuten

Benutzeravatar von Allan of Sydney
Allan aus Sydney

Der Kontext meiner Herausforderung

Ich baue einen Headless WordPress / WooCommerce Store.

Wenn Sie mit dem Konzept eines Headless-CMS nicht vertraut sind, ziehe ich den Inhalt des Shops (Produkte und ihre Bilder, Text) über die WordPress / WooCommerce-REST-API. Auf diese Weise habe ich den Vorteil eines CMS-Dashboards für meinen Kunden, während ich in einer modernen Sprache / Bibliothek entwickeln kann, in meinem Fall – React!

Wenn möglich möchte ich den Checkout in WordPress/WooCommerce/PHP beibehalten. Je nach Projekt, auf das ich diesen Code/Boilerplate anwende, vermute ich, dass ich Zahlungs-Gateways hacken und ändern muss, und dies sicher und PCI-konform zu machen, wird in PHP/WordPress viel einfacher sein – dafür gibt es eine ganze Reihe von Plugins.

Das bedeutet, dass der gesamte Shop / das gesamte Frontend in React leben wird, mit Ausnahme des Warenkorbs, in dem der Benutzer zum CMS-Frontend (WordPress, PHP) weitergeleitet wird, wenn er seine Bestellung abschließen möchte.

Die Herausforderung

Dies macht die Verwaltung von Cookies für die Sitzung ziemlich unintuitiv und unorthodox. Wenn der Benutzer vom Geschäft (React-Site) zur Kasse (WooCommerce/PHP-Site) umgeleitet wird, muss die Warenkorbsitzung zwischen den beiden Sites bestehen bleiben.

Außerdem werden Anfragen an WooCommerce über den Node/Express-Server geleitet, auf dem mein React-Client sitzt. Ich mache das, weil ich die WordPress-Adresse verschleiert halten möchte, und so kann ich GraphQL anwenden, um meine Anfragen und Antworten zu bereinigen. Dieses Problem besteht darin, dass bei diesem Prozess die Cookies verloren gehen, weil mein Client und mein CMS über einen Mittelsmann (meinen Node-Server) kommunizieren – ich benötige zusätzliche Logik, um meine Cookies manuell zu verwalten.

Grobe Skizze der Lösungsarchitektur

Der Code

Wenn ich versuche, etwas zu einem Warenkorb hinzuzufügen, treffe ich von einem Aktionsersteller (ich verwende Redux für die Zustandsverwaltung) auf den entsprechenden API-Endpunkt auf meinem Node/Express-Server:

export const addToCart = (productId, quantity) => async (dispatch) => {
  dispatch({type: ADD_TO_CART});
  try {
    // Manually append cookies somewhere here
    const payload = await axios.get(`${ROOT_API}/addtocart?productId=${productId}&quantity=${quantity}`, {
      withCredentials: true
    });
    dispatch(addToSuccess(payload));
  } catch (error) {
    dispatch(addToCartFailure(error));
  }
};

Dann mache ich auf dem Node/Express-Server meine Anfrage an WooCommerce:

app.get('/api/addtocart', async (req, res) => {
    try {
      // Manually retrieve & append cookies somewhere here
      const productId = parseInt(req.query.productId);
      const quantity = parseInt(req.query.quantity);
      const response = await axios.post(`${WP_API}/wc/v2/cart/add`, {
        product_id: productId,
        quantity
      });
      return res.json(response.data);
    } catch (error) {
      // Handle error
      return res.json(error);
    }
});

  • Hast du es mit der Einstellung versucht define( 'COOKIE_DOMAIN', 'somedomain.com' ); in deiner wp-config.phpstellt dies sicher, dass WordPress Cookies in der Hauptdomäne und nicht in der Unterdomäne generiert, und beide teilen die Cookies mit allem Extra, also keine Codekomplexität wie Sie sie derzeit haben

    – Tarun Lalwani

    15. Juli 2018 um 17:21 Uhr

  • Danke für den Tipp @TarunLalwani – ich habe es nicht ausprobiert, ich werde es versuchen! Es wäre erstaunlich, wenn dies alle meine Keksprobleme lösen könnte. Mein anfängliches Zögern wäre das EIN) Ob WooCommerce diese Option berücksichtigt oder nicht, und B) Dass alle REST-API-Aufrufe von React an WordPress serverseitig (node, express) und nicht clientseitig (react) erfolgen. Ich stelle mir vor, dass dies die Fähigkeit von Axios (meinem HTTP-Client) beeinträchtigen würde, den Anfragen, die ich an WordPress sende, automatisch relevante Browser-Cookies hinzuzufügen

    – Allan aus Sydney

    15. Juli 2018 um 21:36 Uhr

  • A) Kann ausgeräumt werden – Ich habe eine Bestätigung von den Devs gefunden, dass: “WooCommerce verwendet die gleichen Cookie-Einstellungen wie WordPress (z. B. WordPress-Login-Cookie)”

    – Allan aus Sydney

    15. Juli 2018 um 22:10 Uhr


  • Für B müssen Sie alle Browser-Cookies in Ihrer Anfrage an WP senden und dasselbe auch für Rückgabe-Cookies tun

    – Tarun Lalwani

    16. Juli 2018 um 2:46 Uhr

  • Sie haben Recht, ich muss meine Cookies vom Client (React) an den Server (Node) weitergeben, bevor ich auf das CMS (WooCommerce über REST) ​​treffe, und dann ähnlich umgekehrt, indem ich Cookies für den Client innerhalb des Knotens setze Server. Es ist kompliziert, aber es ist fast geschafft!

    – Allan aus Sydney

    16. Juli 2018 um 10:18 Uhr

Benutzeravatar von Allan of Sydney
Allan aus Sydney

Mit den Hinweisen von @TarunLalwani (tausend Dank!) in seinen Kommentaren ist es mir gelungen, eine Lösung zu formulieren.

Cookie-Domain-Einstellung

Da ich mit zwei separaten Websites arbeitete, musste ich sicherstellen, dass sich beide auf derselben Domäne befanden und dass die Domäne in allen Cookies festgelegt war, damit dies funktionierte. Dadurch wurde sichergestellt, dass Cookies in meine Anfragen zwischen dem Node / Express-Server (sitzen auf z. somedomain.com) und das WooCommerce CMS (sitzt auf zB. wp.somedomain.com), anstatt exklusiv für die wp.somedomain Unterdomäne. Dies wurde durch Einstellung erreicht define( 'COOKIE_DOMAIN', 'somedomain.com' ); in meinem wp-config.php auf dem CMS.

Manuelles Abrufen und Setzen von Cookies

Mein Code benötigte eine erhebliche zusätzliche Logik, damit Cookies eingeschlossen werden konnten, während Anfragen über meinen Node/Express-Server durch den Client geleitet wurden.

In React musste ich überprüfen, ob das Cookie existierte, und wenn ja, musste ich es im Header meiner GET-Anfrage an den Node / Express-Server senden.

import Cookies from 'js-cookie';

export const getSessionData = () => {
  // WooCommerce session cookies are appended with a random hash.
  // Here I am tracking down the key of the session cookie.
  const cookies = Cookies.get();
  if (cookies) {
    const cookieKeys = Object.keys(cookies);
    for (const key of cookieKeys) {
      if (key.includes('wp_woocommerce_session_')) {
        return `${key}=${Cookies.get(key)};`;
      }
    }
  }
  return false;
};

export const addToCart = (productId, quantity) => async (dispatch) => {
  dispatch({type: ADD_TO_CART});
  const sessionData = getSessionData();
  const config = {};
  if (sessionData) config['session-data'] = sessionData;
  console.log('config', config);
  try {
    const payload = await axios.get(`${ROOT_API}/addtocart?productId=${productId}&quantity=${quantity}`, {
      withCredentials: true,
      headers: config
    });
    dispatch(addToSuccess(payload));
  } catch (error) {
    dispatch(addToCartFailure(error));
  }
};

Auf dem Node/Express Server musste ich prüfen, ob ich ein Cookie eingebunden hatte (gespeichert in req.headers mit dem Schlüssel session-data – Die Verwendung war illegal Cookie als Schlüssel hier) vom Client, und wenn ja, hängen Sie das an den Header meiner Anfrage an, die an mein CMS geht.

Wenn ich kein angehängtes Cookie fand, bedeutete dies, dass dies die erste Anfrage in der Sitzung war, also musste ich das Cookie manuell aus der Antwort holen, die ich vom CMS zurückbekam, und es auf dem Client speichern (setCookieFunc).

app.get('/api/addtocart', async (req, res) => {
  try {
    const productId = parseInt(req.query.productId);
    const quantity = parseInt(req.query.quantity);
    const sessionData = req.headers['session-data'];
    const headers = {};
    if (sessionData) headers.Cookie = sessionData;
    const response = await axios.post(`${WP_API}/wc/v2/cart/add`, {
      product_id: productId,
      quantity
    }, { headers });
    if (!sessionData) {
      const cookies = response.headers['set-cookie'];
      const setCookieFunc = (cookie) => {
        const [cookieKeyValue, ...cookieOptionsArr] = cookie.split('; ');
        const cookieKey = cookieKeyValue.split('=')[0];
        const cookieValue = decodeURIComponent(cookieKeyValue.split('=')[1]);
        const cookieOptions = { };
        cookieOptionsArr.forEach(option => (cookieOptions[option.split('=')[0]] = option.split('=')[1]));
        if (cookieOptions.expires) {
          const expires = new Date(cookieOptions.expires);
          cookieOptions.expires = expires;
        }
        res.cookie(cookieKey, cookieValue, cookieOptions);
      };
      cookies.map(cookie => setCookieFunc(cookie));
    }
    return res.json(response.data);
  } catch (error) {
    // Handle error
    return res.json(error);
  }
});

Ich bin mir nicht sicher, ob dies die eleganteste Lösung für das Problem ist, aber es hat für mich funktioniert.


Anmerkungen

Ich habe die verwendet js-Cookie Bibliothek für die Interaktion mit Cookies auf meinem React-Client.


Fallstricke

Wenn Sie versuchen, dies in Ihrer Entwicklungsumgebung (unter Verwendung von localhost) zum Laufen zu bringen, müssen Sie zusätzliche Arbeit leisten. Siehe Cookies auf Localhost mit expliziter Domäne

1386740cookie-checkTeilen von Cookies zwischen Websites auf derselben Domain – Headless / Entkoppeltes CMS

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy