JavaScript >> Javascript 文檔 >  >> Tags >> API

如何使用 JavaScript Fetch API 執行 HTTP 請求

如何使用 JavaScript fetch API 在瀏覽器和 Node.js 中執行 HTTP 請求。

開始使用

對於本教程,我們將使用 CheatCode Next.js 樣板來展示 02 的用法 在客戶端和 CheatCode Node.js Server Boilerplate 上展示 13 的用法 在服務器上。

首先,讓我們克隆 Next.js 樣板:

終端

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

接下來,28 進入項目並安裝它的依賴項:

終端

cd client && npm install

之後,繼續啟動開發服務器:

終端

npm run dev

接下來,在另一個選項卡或終端窗口中,我們要克隆 Node.js 樣板:

終端

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

接下來,36 進入項目並安裝依賴項:

終端

cd server && npm install

在我們啟動開發服務器之前,我們需要安裝兩個額外的依賴:4554

終端

npm i isomorphic-fetch faker

安裝這兩個後,繼續啟動服務器:

終端

npm run dev

有了這個,我們就可以開始了。

在 Node.js 中使用 Fetch API

雖然看起來有點落後,但對於本教程,我們將從服務器端開始工作,然後轉到客戶端。原因是我們要設置一些我們可以執行的測試路線 68 針對客戶端的請求。在此期間,我們將快速了解如何使用 77 在 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...
  });
};

在我們上面克隆的 Node.js 樣板中,已經為我們配置了 Express.js 服務器。在上面的文件中,樣板文件設置了它支持的各種 API(默認情況下,只是一個 GraphQL API)。傳遞到從該文件導出的函數中的是 Express 8196 中為我們設置的實例 項目中的文件。

在這裡,在我們設置 GraphQL 服務器 100 的函數調用下方 (我們不會使用這個,我們只是為了避免混淆而把它叫出來),我們定義了三個路由:

  1. 112 使用 121 它創建了一個只接受 HTTP GET 請求的 Express.js 路由。
  2. 133 使用 144 它創建了一個只接受 HTTP POST 請求的 Express.js 路由。
  3. 156 使用 161 這是一個 Express.js 路由,它只接受 HTTP GET 請求,將是我們使用 171 的地方 從第三方 API 獲取數據。

/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...
  });
};

添加 183 195 的頂部 我們之前安裝的依賴,這裡我們填寫204 我們的 219 版本 路線。在內部,我們的目標是返回一些測試數據(我們將執行 222 稍後從客戶端請求並期望此數據作為回報)。對於我們的數據,我們使用了一點 JavaScript 技巧。

236 我們在這裡映射的意思是“在內存中創建一個新的 JavaScript 數組,其中包含 50 個元素(這些將只是 240 值),然後使用 253 '傳播'或'解壓'該數組 展開操作符——放入包裹該語句的數組中。”我們的目標是獲得 50 個“佔位符”,我們可以使用 JavaScript 262 替換它們 方法。

我們看到這裡發生了這種情況,為 50 個佔位符元素中的每一個返回一個描述虛構用戶的對象。反過來,這將返回一個包含 50 個用戶對象的數組。為了“彌補”這些用戶,我們使用 270 庫——一個用於創建虛假測試數據的工具——為我們地圖的每次迭代創建一個真實的測試用戶(在此處了解有關 Faker 的 API 的更多信息)。

最後,在我們創建了 286 數組之後 ,我們採用該變量並使用 298 來自 Express.js 的對象(這是作為我們路由的回調函數的第二個參數傳遞的),並做兩件事:

  1. 設置HTTP狀態碼為308 使用 318 方法(這是“成功”的標準 HTTP 代碼)。
  2. 使用“鏈接”方法的能力,調用 329 設置336後的方法 在 345 ,傳入我們的 356 的字符串化版本 變量(包含我們的用戶數組)。

在這裡,使用 362 是必要的,因為只能發送字符串來響應 HTTP 請求。稍後,在客戶端,我們將學習如何將該字符串轉換回 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...
  });
};

接下來,對於 374 我們的 384 版本 路線,我們保持簡單。因為 HTTP POST 請求的目的是創建插入 一些數據到數據庫中(或將其交給另一個數據源),在這裡,我們只是註銷 397 的內容 這是通過請求發送給我們的解析內容。這將在稍後派上用場,因為我們將看到我們如何傳遞給 408 的選項 request 判斷我們傳遞給客戶端的 body 是否到達服務器。

最後,在這裡,我們重複我們在 419 中看到的相同模式 427 的版本 , 調用 432 ,設置445453 , 並返回一個字符串響應(這裡只是一個表示用戶收到的純字符串)。

/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)));
      }
    );
  });
};

對於我們的最終路線,我們創建另一個 460 路線,這次使用路線 477 .對於這條路線,我們將使用服務器端 487 調用第三方 API 並將我們獲取的數據發送回應用程序的客戶端。在頂部,您可以看到我們已經導入了 491 我們之前安裝的依賴項為 507 .

在這裡,我們調用 517 免費 JSON 佔位符 API 上的端點,該 API 向我們返回一個對像數組,其中包含指向股票照片的指針。

在我們調用 520 之後 ,我們鏈接在 532 回調——這表示我們期望 546 返回一個 JavaScript Promise——將一個函數傳遞給那個 559 方法。在該函數內部,我們採用 566 作為參數添加到我們的請求中,同時添加一個 574 我們函數之前的關鍵字。

我們這樣做是因為在下一行,我們調用 589 在調用 593 之前 .這裡的想法是 603 不是由 618 交給我們的 以任何特定格式。相反,我們採用原始的 625 並在該 638 上使用幾種方法之一 對象,我們將響應轉換為我們想要/需要的格式。

這裡,645 是說要轉換 656 轉換成 JSON 格式。我們使用 661 這裡是因為我們期望 676 (以及它的兄弟方法,如 688 ) 返回一個 JavaScript Promise。使用 697 ,我們說“等到這個函數返回一個值,我們可以設置為我們的 701 變量然後繼續下一行。”

在下一行,我們看到了對 710 的熟悉調用 ,確保 725 我們的數據,然後將其發送回應用程序客戶端發出的請求。

這對服務器來說是這樣的!接下來,我們要跳到客戶端,看看739如何 在瀏覽器中工作。

在瀏覽器中使用 Fetch API

進入我們之前克隆的 Next.js 樣板,首先,我們將使用 Next.js 的基於頁面的路由功能在客戶端上創建一個新路由,我們可以在其中測試我們的 744 來電:

/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;

在 Next.js 中,頁面(自動轉換為路由或 URL)是使用 React.js 組件定義的。在這裡,我們使用基於函數的方法在 React 中定義一個組件,該組件由一個普通的 JavaScript 函數組成,該函數返回一些 JSX 標記(為在 React 中編寫組件而構建的標記語言)。

在該函數的主體中,我們也可以定義其他函數並調用 React 獨有的一種特殊類型的函數,稱為鉤子。

從函數體內部開始,我們可以看到對這些鉤子函數之一的調用 752 (從頂部導入)這將允許我們設置一個動態狀態值,然後在我們的 JSX 標記中訪問該值以及在我們的函數組件主體中定義的其他函數(一個稱為“閉包函數”的概念,或者在函數中定義的函數在 JavaScript 中)。

這裡,761 正在說“創建狀態值的實例,將默認值設置為空數組 774 。”

對於該調用的返回值,我們期望返回一個包含兩個值的數組:第一個是當前值 787 第二個是我們可以用來更新的函數 該值 790 .在這裡,我們使用 JavaScript 數組解構來訪問數組的內容,同時將變量分配給數組中這些位置的值。

為了澄清這一點,如果我們將這一行寫成 804 ,我們需要遵循這條線:

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

使用數組解構,我們可以完全避免這種情況。

跳過我們的佔位符函數,接下來查看我們從 811 返回的 JSX 標記 組件函數(Next.js 將為我們的頁面呈現的內容),我們可以看到我們的實際 UI 非常簡單:我們正在呈現三個按鈕和一個 823 塊。

這裡的想法是我們的每個 838 都有一個按鈕 請求類型,後跟一個代碼塊,我們在其中呈現對每個請求的響應(由單擊按鈕觸發)。在這裡,我們可以看到 847 我們使用數組解構從我們對 852 的調用中“提取”的變量 被傳遞到 863 標籤嵌套在我們的 879 中 標籤。這是我們最終存儲來自 883 的響應數據的地方 請求(並在屏幕上查看該數據)。

查看每個按鈕,我們可以看到 894 屬性被賦值。對於前兩個按鈕——我們將負責執行我們的 906 請求示例——我們調用上面定義的函數913 ,傳入一個描述我們想要調用的資源或路徑的字符串(這會更有意義)。

對於最後一個按鈕,我們只需傳遞函數 920 直接,因為我們在調用該函數時不需要傳遞任何參數。

/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;

查看931 我們在下面提示的函數,我們可以看到我們為資源名稱傳遞的字符串被定義為參數 941 關於我們的功能。在該函數內部,我們設置了對 951 的調用 .您會注意到,與服務器不同的是,我們沒有導入 962 從任何地方。

這是因為 977 作為 global 內置於現代瀏覽器中 值(意味著它會在瀏覽器的任何地方自動定義)。

查看我們的調用,就像我們之前看到的那樣,我們調用 988 傳遞一個 URL 作為第一個參數。在這種情況下,我們傳遞 999 之一的 URL 我們之前在服務器上定義的路由。這將根據為 1005 傳遞的值動態變化 , 到 10181027 .

作為 1030 的第二個參數 ,我們傳遞一個選項對象。在這裡,我們只傳遞了一個屬性 1041 .正如我們在實現 POST 請求時將看到的,我們在此處傳遞的內容決定了我們的請求的實際行為方式。在這種情況下,我們告訴 1052 在發送請求時將瀏覽器的 cookie 包含在請求標頭中。雖然我們沒有在服務器上驗證我們的請求,但如果您期望 1060,請務必注意這一點 表現得像一個瀏覽器(它會自動發送帶有自己請求的 cookie)。

最後,在這裡,在 1075 回調(記住,1088 將返回給我們一個 JavaScript Promise),我們使用 async/await 模式到 1096 以 JavaScript 友好的格式(數組或對象)獲取返回數據,然後調用 1103 我們從 1110 得到的函數 鉤子函數來設置響應數據以顯示在我們的 1120 標記。

/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;

接下來,對於我們的 1134 函數,我們重複與我們的 GET 請求類似的過程。但是,在這裡,我們硬編碼了我們的 URL(我們在服務器上只有一個 POST 路由),並且因為我們正在執行一個不是 GET 的請求,所以我們設置了一個 1140 1151 的選項 .如果我們這樣做 這樣做,1163 將假設我們正在嘗試執行 GET 請求或“獲取”一些數據。

在此下方,我們可以看到相同的 1173 作為我們的 GET 請求(再次,這裡純粹是為了提高意識)。接下來是重要的部分,因為這是一個POST請求,我們添加一個1189 選項設置為帶有一些測試數據的字符串化 JavaScript 對象。請記住,HTTP 請求只能來回傳遞字符串。為了使這項工作,在 1192 選項,我們添加 HTTP 1207 標頭,將其設置為 1217 .這個很重要。這會通知服務器,我們在正文中發送的數據應該被解析為 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);
};

為了快速理解這一點,在我們應用程序的服務器端,我們使用的 Node.js 樣板文件有一個稱為 1222 的東西 每當請求進入服務器時運行的函數,就在它被移交給我們的 Express.js 路由之前。在這裡,我們可以在底部看到將HTTP請求體解析為JSON格式的中間件函數。

如果我們沒有 設置 1234 1246 中的標頭 請求返回客戶端,我們的請求正文 (1252 在我們服務器上的路由處理程序中)將是一個空對象。然而,一旦我們設置了這個標頭,響應我們請求的服務器就知道“做什麼”並按預期接收我們的請求正文。

/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;

回到我們的 1265 客戶端上的函數,在 1271 回調,我們使用與之前看到的 async/await 類似的流程,但是這一次,而不是 1286 我們使用 1299 .這是因為我們從服務器發回的 POST 請求響應只是一個純字符串(與我們其他請求中的字符串化對象相反)。一旦我們得到我們的 1304 ,我們將其彈出以聲明 1319 .

而已!現在我們準備試一試:

總結

在本教程中,我們學習瞭如何使用 JavaScript 1322 執行 HTTP 請求 API。我們從服務器開始,定義從客戶端發送請求的路由,同時學習如何使用 1330 通過 1344 Node.js 中的庫。接下來,在客戶端,我們學習瞭如何運行 HTTP GET 和 POST 請求,了解傳遞的正確選項以確保我們的服務器能夠理解我們的請求。


Tutorial JavaScript 教程
  1. Document.write() 拋出意外的令牌“非法”

  2. 在 ReactJS 中創建提及和標籤

  3. 使用 React 和 Django 的攝入量表

  4. TIL 如何解決與 Yarn(和 NPM)的依賴衝突

  5. 教程:如何使用 React、Mapbox 和 Bootstrap 創建全球疫苗接種儀表板

  6. 是否可以在 v-for 中放一顆子彈?

  7. 在 JavaScript 中檢查日期

  1. 將表單值保存到 localStorage

  2. 帶有標記的 React 中的功能標誌

  3. 使用 Next.js 製作多語言網站 - 第 1 部分

  4. 在 Ionic Vue 中使用堆疊模式

  5. 為什麼我的微調器 GIF 在運行 jQuery ajax 調用時停止?

  6. Python 如何幫助您學習 ES6

  7. 將類型引用為索引時出錯

  1. 將 NextJS 與無頭 WordPress 一起使用

  2. 分析肯尼亞電力計劃中斷數據

  3. 使用 JS:變量

  4. JavaScript 數學函數和運算符