Jak používat rozhraní JavaScript Fetch API k provádění požadavků HTTP

Jak používat API načítání JavaScriptu k provádění požadavků HTTP v prohlížeči a v Node.js.

Začínáme

V tomto tutoriálu použijeme CheatCode Next.js Boilerplate k předvedení použití 05 na klientovi a CheatCode Node.js Server Boilerplate pro předvedení použití 16 na serveru.

Chcete-li začít, pojďme naklonovat základní verzi Next.js:

Terminál

git clone https://github.com/cheatcode/nextjs-boilerplate client

Dále 21 do projektu a nainstalujte jeho závislosti:

Terminál

cd client && npm install

Poté pokračujte a spusťte vývojový server:

Terminál

npm run dev

Dále v jiné kartě nebo okně terminálu chceme naklonovat základní verzi Node.js:

Terminál

git clone https://github.com/cheatcode/nodejs-server-boilerplate server

Dále 39 do projektu a nainstalujte závislosti:

Terminál

cd server && npm install

Než spustíme vývojový server, musíme nainstalovat dvě další závislosti:48 a 57 :

Terminál

npm i isomorphic-fetch faker

S těmito dvěma nainstalovanými pokračujte a spusťte server:

Terminál

npm run dev

Díky tomu jsme připraveni začít.

Použití rozhraní Fetch API v Node.js

I když se to může zdát trochu zaostalé, v tomto tutoriálu začneme pracovat na straně serveru a poté přejdeme ke klientovi. Důvodem je, že nastavíme nějaké testovací trasy, které můžeme provést 60 požadavky vůči klientovi. Když už jsme u toho, také se rychle podíváme na to, jak používat 70 v prostředí serveru Node.js.

/server/api/index.js

import graphql from "./graphql/server";

export default (app) => {
  graphql(app);

  app.get("/users", (req, res) => {
    // We'll implement an HTTP GET test route here...
  });

  app.post("/users", (req, res) => {
    // We'll implement an HTTP POST test route here...
  });

  app.get("/photos", (req, res) => {
    // We'll implement a server-side fetch request here...
  });
};

Uvnitř standardu Node.js, který jsme naklonovali výše, je pro nás již nakonfigurovaný server Express.js. Ve výše uvedeném souboru standard nastavuje různá rozhraní API, která podporuje (ve výchozím nastavení pouze GraphQL API). Do funkce exportované z tohoto souboru je předán Express 84 instance, která je pro nás nastavena v 98 soubor v projektu.

Zde pod voláním funkce, kde jsme nastavili náš GraphQL server 103 (nepoužijeme to, jen to voláme, abychom ušetřili zmatek), definujeme tři trasy:

  1. 114 pomocí 124 který vytvoří cestu Express.js, která přijímá pouze požadavky HTTP GET.
  2. 136 pomocí 146 který vytvoří cestu Express.js, která přijímá pouze požadavky HTTP POST.
  3. 159 pomocí 163 což je cesta Express.js, která přijímá pouze požadavky HTTP GET a bude tam, kde používáme 174 získat data z rozhraní API třetí strany.

/server/api/index.js

import faker from "faker";
import graphql from "./graphql/server";

export default (app) => {
  graphql(app);

  app.get("/users", (req, res) => {
    const users = [...Array(50)].map(() => {
      return {
        name: {
          first: faker.name.firstName(),
          last: faker.name.lastName(),
        },
        emailAddress: faker.internet.email(),
        address: {
          street: faker.address.streetAddress(),
          city: faker.address.city(),
          state: faker.address.state(),
          zip: faker.address.zipCode(),
        },
      };
    });

    res.status(200).send(JSON.stringify(users, null, 2));
  });

  app.post("/users", (req, res) => {
    // We'll implement an HTTP POST test route here...
  });

  app.get("/photos", (req, res) => {
    // We'll implement a server-side fetch request here...
  });
};

Přidání 185 nahoru pro 197 závislost, kterou jsme nainstalovali dříve, zde vyplňujeme 206 verzi našeho 212 trasa. Uvnitř je naším cílem vrátit nějaká testovací data (provedeme 225 požadovat od klienta později a očekávat tyto údaje na oplátku). Pro naše data používáme malý trik s JavaScriptem.

239 které zde mapujeme, říká „vytvořte nové pole JavaScriptu v paměti s 50 prvky (budou to jen 243 hodnoty) a poté toto pole „rozbalte“ nebo „rozbalte“ pomocí 256 spread operátor – do pole zabalujícího tento příkaz." Naším cílem je získat 50 „zástupných symbolů“, které můžeme nahradit pomocí JavaScriptu 265 metoda.

Vidíme, že se to děje zde a vracíme objekt popisující vytvořeného uživatele pro každý z 50 zástupných prvků. To nám zase vrátí pole s 50 vytvořenými uživatelskými objekty. K „vytvoření“ těchto uživatelů používáme 273 knihovna — nástroj pro vytváření falešných testovacích dat — k vytvoření realistického testovacího uživatele pro každou iteraci naší mapy (další informace o Faker's API zde).

Nakonec, když jsme vytvořili pole 289 , vezmeme tuto proměnnou a použijeme 291 objekt z Express.js (toto je předáno jako druhý argument funkci zpětného volání pro naši trasu) a proveďte dvě věci:

  1. Nastavte stavový kód HTTP na 301 pomocí 311 metoda (toto je standardní HTTP kód pro „úspěch“).
  2. Pomocí možnosti „řetězení“ metod volejte 322 po nastavení 339 na 347 , který předává strunovanou verzi našeho 359 proměnná (obsahující naše pole uživatelů).

Zde pomocí 365 je nezbytný, protože jako odpověď na požadavky HTTP lze odesílat pouze řetězce. Později se na klientovi naučíme, jak tento řetězec převést zpět na pole JavaScript.

/server/api/index.js

import faker from "faker";
import graphql from "./graphql/server";

export default (app) => {
  graphql(app);

  app.get("/users", (req, res) => {
    ...
    res.status(200).send(JSON.stringify(users, null, 2));
  });

  app.post("/users", (req, res) => {
    console.log(req.body);
    res.status(200).send(`User created!`);
  });

  app.get("/photos", (req, res) => {
    // We'll implement a server-side fetch request here...
  });
};

Dále pro 377 verzi našeho 387 trasa, děláme věci jednoduché. Protože záměrem požadavku HTTP POST je vytvořit nebo vložit nějaká data do databáze (nebo je předejte jinému zdroji dat), zde právě odhlašujeme obsah 390 což je analyzovaný obsah, který nám byl zaslán prostřednictvím požadavku. To se bude hodit později, protože uvidíme, jak možnosti předáme 401 požadavek určí, zda se tělo, které předáme klientovi, dostane na server.

Nakonec zde zopakujeme stejný vzor, ​​který jsme viděli v 411 verzi 420 , volání na 435 , nastavením 447 na 456 a odešlete zpět řetězcovou odpověď (zde pouze prostý řetězec označující přijetí uživatele).

/server/api/index.js

import faker from "faker";
import fetch from "isomorphic-fetch";
import graphql from "./graphql/server";

export default (app) => {
  graphql(app);

  app.get("/users", (req, res) => {
    ...
    res.status(200).send(JSON.stringify(users, null, 2));
  });

  app.post("/users", (req, res) => {
    console.log(req.body);
    res.status(200).send(`User created!`);
  });

  app.get("/photos", (req, res) => {
    fetch("https://jsonplaceholder.typicode.com/photos").then(
      async (response) => {
        const data = await response.json();
        res.status(200).send(JSON.stringify(data.slice(0, 50)));
      }
    );
  });
};

Pro naši konečnou trasu vytvoříme další 463 trasu, tentokrát pomocí trasy 479 . Pro tuto cestu použijeme 480 na straně serveru zavoláte rozhraní API třetí strany a odešlete získaná data zpět na klientskou stranu naší aplikace. Nahoře vidíte, že jsme importovali 494 závislost, kterou jsme dříve nainstalovali jako 509 .

Zde zavoláme na 510 koncový bod na bezplatném JSON Placeholder API, které nám vrací pole objektů s ukazateli zpět na fotografie.

Po našem hovoru na 523 , řetězíme na 535 zpětné volání – to znamená, že očekáváme 549 vrátit JavaScript Promise – předání funkce tomuto 552 metoda. Uvnitř této funkce přebíráme 569 k našemu požadavku jako argument a také přidání 576 klíčové slovo před naší funkcí.

Děláme to proto, že na dalším řádku zavoláme na 582 před voláním na 596 . Myšlenka je taková, že 606 není nám předáno 611 v jakémkoli konkrétním formátu. Místo toho vezmeme nezpracovaný 628 a pomocí jedné z mála metod na tom 631 objekt, převedeme odpověď do formátu, který chceme/potřebujeme.

Zde 648 říká převést 659 do formátu JSON. Používáme 664 zde, protože očekáváme 679 (a jeho sourozenecké metody jako 681 ), abyste vrátili příslib JavaScriptu. S 690 , říkáme "počkej, až nám tato funkce vrátí hodnotu, kterou můžeme nastavit na naše 704 proměnnou a poté pokračujte na další řádek."

Na dalším řádku vidíme známé volání 713 , ujistěte se, že 727 naše data před jejich odesláním zpět na požadavek ze strany klienta naší aplikace.

To platí pro server! Dále přejdeme ke klientovi a podíváme se, jak 732 funguje v prohlížeči.

Použití rozhraní Fetch API v prohlížeči

Přesuneme se do standardu Next.js, který jsme naklonovali dříve, začneme tím, že použijeme funkci směrování na základě stránky Next.js k vytvoření nové trasy na klientovi, kde budeme moci otestovat naše 746 volání:

/client/pages/index.js

import React, { useState } from "react";

const Index = () => {
  const [data, setData] = useState([]);

  const getRequestWithFetch = (resource = "") => {
    // We'll make our GET requests using fetch here...
  };

  const postRequestWithFetch = () => {
    // We'll make a our POST request using fetch here...
  };

  return (
    <div>
      <button
        className="btn btn-primary"
        style={{ marginRight: "10px" }}
        onClick={() => getRequestWithFetch("users")}
      >
        GET Request (Users)
      </button>
      <button
        className="btn btn-primary"
        style={{ marginRight: "10px" }}
        onClick={() => getRequestWithFetch("photos")}
      >
        GET Request (Photos)
      </button>
      <button className="btn btn-primary" onClick={postRequestWithFetch}>
        POST Request
      </button>
      <pre style={{ background: "#eee", marginTop: "20px", padding: "20px" }}>
        <code>{data}</code>
      </pre>
    </div>
  );
};

export default Index;

V Next.js jsou stránky (které jsou automaticky převedeny na trasy nebo adresy URL) definovány pomocí komponent React.js. Zde používáme přístup založený na funkcích k definování komponenty v Reactu, která se skládá z prosté JavaScriptové funkce, která vrací nějaké značky JSX (značkový jazyk vytvořený pro vytváření komponent v Reactu).

V těle této funkce také můžeme definovat další funkce a volat speciální typ funkce jedinečný pro React, nazývaný hooks.

Když začneme přímo v těle naší funkce, můžeme vidět volání jedné z těchto funkcí zavěšení 755 (importováno nahoru), což nám umožní nastavit hodnotu dynamického stavu a poté přistupovat k této hodnotě v našem značkování JSX a dalším funkcím definovaným v těle naší funkční komponenty (koncept známý jako „uzavírací funkce“ nebo funkce definované v rámci funkcí v JavaScriptu).

Zde 764 říká "vytváření instance hodnoty stavu, nastavení výchozí hodnoty na prázdné pole 776 ."

Pro návratovou hodnotu tohoto volání očekáváme, že získáme zpět pole se dvěma hodnotami:první je aktuální hodnota 780 a druhá je funkce, kterou můžeme použít k aktualizaci tato hodnota 792 . Zde používáme destrukci pole JavaScript pro přístup k obsahu našeho pole a současné přiřazení proměnných hodnotám na těchto pozicích v poli.

Abychom to objasnili, kdybychom tento řádek napsali jako 807 , museli bychom tento řádek sledovat něčím jako:

const data = state[0];
const setData = state[1];

Pomocí destrukce pole se tomu můžeme zcela vyhnout.

Přeskakování přes naše zástupné funkce a dále se podíváme na označení JSX, které vracíme z našeho 814 komponentní funkce (co Next.js vykreslí pro naši stránku), vidíme, že naše skutečné uživatelské rozhraní je docela jednoduché:vykreslujeme tři tlačítka a 826 blokovat.

Myšlenka je taková, že pro každý z našich 831 máme jedno tlačítko typy požadavků, za nimiž následuje blok kódu, kde vykreslujeme odpověď na každý požadavek (spouští se kliknutím na tlačítko). Zde můžeme vidět 840 proměnnou, kterou jsme "vytrhli" pomocí destrukcí pole z našeho volání na 855 jsou předány do 866 tag vnořený do našeho 872 štítek. Zde budeme nakonec ukládat data odpovědí z našeho 886 požadavky (a uvidíte tato data na obrazovce).

Při pohledu na každé tlačítko vidíme 897 atribut, kterému je přiřazena hodnota. U prvních dvou tlačítek – za jejichž provedení budeme zodpovědní naši 908 příklady požadavků – voláme funkci definovanou výše 916 , předáním řetězce popisujícího zdroj nebo cestu, kterou bychom chtěli volat (toto bude za chvíli dávat větší smysl).

Pro poslední tlačítko pouze předáme funkci 922 přímo, protože při volání této funkce nepotřebujeme předávat žádné argumenty.

/client/pages/index.js

import React, { useState } from "react";

const Index = () => {
  const [data, setData] = useState([]);

  const getRequestWithFetch = (resource = "") => {
    fetch(`http://localhost:5001/${resource}`, {
      credentials: "include",
    }).then(async (response) => {
      const data = await response.json();

      // NOTE: Doing JSON.stringify here for presentation below. This is not required.
      setData(JSON.stringify(data, null, 2));
    });
  };

  const postRequestWithFetch = () => {
    // We'll make a our POST request using fetch here...
  };

  return (
    <div>
      ...
    </div>
  );
};

export default Index;

Při pohledu na 934 funkce, kterou jsme naznačili níže, můžeme vidět řetězec, který jsme předali pro náš název zdroje, jak je definován jako argument 944 na naší funkci. Uvnitř této funkce nastavíme naše volání na 954 . Něco, čeho si všimnete, je, že na rozdíl od serveru neimportujeme 965 odkudkoli.

Důvodem je 979 je integrován do moderních prohlížečů jako globální value (to znamená, že je automaticky definována všude v prohlížeči).

Při pohledu na náš hovor, stejně jako jsme viděli dříve, voláme na 981 předání adresy URL jako prvního argumentu. V tomto případě předáváme adresu URL pro jeden z 992 trasy, které jsme dříve definovali na našem serveru. Toto se dynamicky změní na základě hodnoty předané pro 1008 , buď na 1010 nebo 1020 .

Jako druhý argument pro 1033 , předáme objekt opcí. Zde pouze předáváme jednu vlastnost 1044 . Jak uvidíme, když implementujeme náš požadavek POST, to, co zde předáme, určuje, jak se náš požadavek skutečně chová. V tomto případě říkáme 1055 zahrnout soubory cookie prohlížeče do záhlaví požadavku, když požadavek odešle. I když neověřujeme naše požadavky na serveru, je důležité si to uvědomit, pokud očekáváte 1063 chovat se jako prohlížeč (který automaticky odesílá soubory cookie s vlastními požadavky).

Konečně zde, dole v 1078 zpětné volání (pamatujte, 1081 vrátí nám příslib JavaScriptu), použijeme vzor async/wait na 1099 abyste získali návratová data zpět ve formátu vhodném pro JavaScript – pole nebo objekt – a pak zavolejte 1106 funkce, kterou jsme dostali zpět z našeho 1113 funkce háku pro nastavení dat odezvy pro zobrazení v našem 1123 tag.

/client/pages/index.js

import React, { useState } from "react";

const Index = () => {
  const [data, setData] = useState([]);

  const getRequestWithFetch = (resource = "") => {
    ...
  };

  const postRequestWithFetch = () => {
    fetch(`http://localhost:5001/users`, {
      method: "POST",
      credentials: "include",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "test",
      }),
    }).then(async (response) => {
      const data = await response.text();
      setData(data);
    });
  };

  return (
    <div>
      ...
    </div>
  );
};

export default Index;

Dále pro naše 1137 funkce, opakujeme podobný proces jako náš požadavek GET. Zde však napevno zakódujeme naši adresu URL (na serveru máme pouze jednu cestu POST) a protože provádíme jiný požadavek než GET, nastavíme 1143 možnost 1153 . Pokud to neděláme udělejte to, 1166 bude předpokládat, že se pokoušíme provést požadavek GET nebo "načíst" nějaká data.

Pod tím můžeme vidět stejný 1175 jako náš požadavek GET (zde opět čistě pro povědomí). Dále, důležitá část, protože se jedná o požadavek POST, přidáme 1182 možnost nastavena na stringifikovaný objekt JavaScript s některými testovacími daty. Pamatujte, že požadavky HTTP mohou předávat pouze řetězce tam a zpět. Aby to fungovalo, v 1195 možnost, přidáme HTTP 1200 záhlaví a nastavte jej na 1210 . Toto je důležité. Tím sdělíte serveru, že data, která odesíláme v těle, by měla být analyzována jako data JSON.

/server/middleware/bodyParser.js

import bodyParser from "body-parser";

export default (req, res, next) => {
  const contentType = req.headers["content-type"];

  if (contentType && contentType === "application/x-www-form-urlencoded") {
    return bodyParser.urlencoded({ extended: true })(req, res, next);
  }

  return bodyParser.json()(req, res, next);
};

Abychom to pochopili, rychle, na straně serveru naší aplikace má standard Node.js, který používáme, něco známého jako 1225 funkce, která se spustí vždy, když na server přijde požadavek, těsně předtím, než je předán do našich tras Express.js. Zde vidíme ve spodní části middlewarovou funkci, která analyzuje tělo požadavku HTTP do formátu JSON.

Pokud neudělali nastavte 1237 záhlaví v našem 1249 požadavek zpět na klienta, naše tělo požadavku (1254 v naší obslužné rutině trasy na serveru) by byl prázdný objekt. Jakmile však tuto hlavičku nastavíme, server reagující na náš požadavek ví „co má dělat“ a obdrží naše tělo požadavku tak, jak bylo zamýšleno.

/client/pages/index.js

import React, { useState } from "react";

const Index = () => {
  const [data, setData] = useState([]);

  const getRequestWithFetch = (resource = "") => {
    ...
  };

  const postRequestWithFetch = () => {
    fetch(`http://localhost:5001/users`, {
      method: "POST",
      credentials: "include",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "test",
      }),
    }).then(async (response) => {
      const data = await response.text();
      setData(data);
    });
  };

  return (
    <div>
      ...
    </div>
  );
};

export default Index;

Zaměřujeme se zpět na naše 1268 funkci na klientovi v 1274 zpětného volání používáme podobný postup, jaký jsme viděli dříve u async/await, tentokrát však namísto 1289 používáme 1292 . Je to proto, že odpověď, kterou posíláme zpět ze serveru na náš požadavek POST, je pouze prostý řetězec (na rozdíl od stringifikovaných objektů jako v našich jiných požadavcích). Jakmile budeme mít naše 1307 , zobrazíme jej do stavu 1312 .

A je to! Nyní jsme připraveni si to probrat:

Zabalení

V tomto tutoriálu jsme se naučili provádět požadavky HTTP pomocí JavaScriptu 1324 API. Začali jsme na serveru, kde jsme definovali trasy, na které budeme odesílat požadavky od klienta, a také jsme se naučili, jak používat 1333 prostřednictvím 1347 knihovny z Node.js. Dále jsme se na klientovi naučili spouštět požadavky HTTP GET a POST a dozvěděli jsme se o správných možnostech, které je třeba předat, aby náš server porozuměl našemu požadavku.