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

使用 Node 和 Express 構建 REST API

簡介

REST API 是 Web 服務發送和接收數據的行業標準方式。他們使用 HTTP 請求方法來促進請求-響應循環,通常使用 JSON 傳輸數據,很少使用 HTML、XML 和其他格式。

為簡單起見,我們不會使用數據庫,因此您不需要使用數據庫的經驗。我們將使用一個簡單的 JavaScript 數組來存儲我們的數據。

什麼是 REST API?

REST (Representational State Transfer) 是一種用於構建 Web 服務並與之通信的標準架構。它通常要求網絡上的資源以文本格式(如 JSON、HTML 或 XML)表示,並且可以通過一組預定的操作來訪問或修改。鑑於我們通常構建 REST API 來利用 HTTP 而不是其他協議,因此這些操作對應於 HTTP 方法,例如 GET、POST 或 PUT。

在數據集合上,例如書籍,我們需要經常執行一些操作,歸結為 - 創建 , 閱讀 , 更新刪除 (也稱為 CRUD 功能 )。

顧名思義,API(應用程序編程接口)是定義不同軟件組件之間交互的接口。 Web API 定義了可以向組件發出哪些請求(例如,獲取書籍列表的端點)、如何發出請求(例如,GET 請求)以及它們的預期響應。

什麼是快遞?

ExpressJS 是 Node.js 最流行的 HTTP 服務器庫之一,默認情況下它對 API 開發並不友好。使用 Express,我們通過抽像出設置服務器所需的樣板來簡化 API 開發,這使得開發更快、更易讀、更簡單。您可以在幾秒鐘內啟動一個原型 API,只需幾行代碼。

雖然它的主要用途是通過合理的默認值來簡化事情,但它可以使用名為 “中間件”的函數進行高度定制 .

注意: Express 非常輕量級並且構建在 頂部 的中間件。使用中間件,您可以擴展和擴展其功能,使其超出默認已存在的功能。

儘管我們只打算在本指南中構建一個 REST API,但 ExpressJS 框架不僅限於此 - 託管靜態文件、執行服務器端渲染,甚至將其用作代理服務器並不少見,而且天空的附加中間件的限制。

HTTP 請求類型

在構建 REST API 之前,我們需要掌握幾種類型的 HTTP 方法。這些是對應於 CRUD 任務的方法:

  • POST :用於提交數據,一般用於create 新實體或編輯現有實體。
  • GET :用於向服務器請求數據,一般用於讀取 數據。
  • PUT :用於將資源完全替換為提交的資源,一般用於更新 數據。
  • DELETE :用於刪除 來自服務器的實體。

注意: 請注意,您可以使用 POSTPUT 編輯存儲的數據。您可以自由選擇是否要使用 PUT 因為它可以完全省略。不過,請與您使用的 HTTP 動詞保持一致。如果您使用的是 POST 同時創建和更新,然後不要使用 PUT 方法。

我們要建造什麼

讓我們創建一個簡單的應用程序來存儲有關書籍的信息。在這個應用程序中,我們將存儲有關圖書的 ISBN、書名、作者、出版日期、出版商和頁數的信息。

當然,API 的基本功能將是 CRUD 功能。我們希望能夠向它發送請求以創建、讀取、更新和刪除 Book 實體。當然,API 可以做的遠不止這些——為用戶提供獲取統計數據、摘要、調用其他 API 等的端點。

非 CRUD 功能依賴於應用程序,並且根據您項目的性質,您可能會有其他端點。但是,幾乎沒有項目可以沒有 CRUD。

為了避免編造書籍數據——讓我們使用來自 GitHub 的數據集來獲取有關書籍的一些示例詳細信息。

設置項目

首先,讓我們初始化一個新的 Node.js 項目:

$ npm init

根據您的要求填寫請求的信息 - 您不必填寫所有字段,但它們是為項目設置可識別數據的簡單方法。 name 等字段 與將應用程序發佈到 Node Package Manager 更相關 ,以及其他領域。

或者,您可以通過添加 -y 來使用默認設置 標記呼叫:

$ npm init -y

無論哪種方式 - 您最終都會得到一個帶有 package.json 的項目 文件。這是一個 json 包含所有相關元數據的文件 在您的項目上,默認情況下會按照以下方式顯示:

{
  "name": "app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "keywords": [],
  "description": ""
}

應用程序的“entry”/“main”是運行以正確啟動項目的文件 - 通常是您的主腳本和 index.js 默認情況下。

此外,您的應用程序和“腳本”的版本在這裡!您可以在 "scripts" 中提供任意數量的自定義命令 部分,帶有與別名關聯的命令。這裡,test alias 是 echo 的包裝器 聲明。

您將通過以下方式運行應用程序的測試:

$ npm test

> [email protected] test /Users/david/Desktop/app
> echo "Error: no test specified" && exit 1

Error: no test specified

通常,有一個 start 當我們想要啟動一個應用程序時,它會屏蔽一個或多個應該運行的進程的別名。在基本形式中 - 我們只是運行帶有節點的索引頁面:

{
  "name": "app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "keywords": [],
  "description": ""
}

除了 node index.js 之外,您可以放置​​任意數量的命令 作為 start 腳本和當你運行 npm start - 他們都會跑:

$ test npm start    

> [email protected] start /Users/david/Desktop/app
> node index.js

注意: 由於我們在啟動腳本中只有一個命令,它在功能上等同於調用 $ node index.js 在命令行中啟動應用程序。

現在你已經熟悉了項目,讓我們安裝 Express!

$ npm install --save express

在目錄中創建一個新文件,旁邊是 node_modules 目錄。 package-lock.json 文件跟踪您的依賴項並包含它們的版本和名稱:

{
  "name": "app",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "accepts": {
      "version": "1.3.7",
      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
      "requires": {
        "mime-types": "~2.1.24",
        "negotiator": "0.6.2"
      }
    },
    ...

node_modules 目錄實際上託管了依賴項的代碼,並且可以很快變得很大。僅通過安裝 Express,我們已經在 package-lock.json 中安裝和跟踪了大量模塊 文件。

事實上,這些模塊很小,所以無論如何都不是問題。通過使用 package-lock.json 文件,另一個客戶端無法知道要下載哪些依賴項以及使用哪些版本才能正確啟動您的應用程序。

注意: 使用 Git 等工具進行版本控制時,不要被認為是一種很好的做法 版本您在應用程序中使用的模塊的源代碼。實際上 - 不要跟踪或推送 node_modules 到存儲庫。其他人可以根據關鍵的package-lock.json下載依賴 這會在他們使用 npm 運行應用程序時自動發生 .

創建一個簡單的端點

現在,讓我們開始構建一個簡單的“Hello World” 應用程序。它將有一個簡單的端點,它只返回一條消息作為對我們獲取主頁請求的響應。

首先,讓我們創建一個名為 hello-world.js 的文件 :

$ nano hello-world.js

然後,讓我們在其中導入 Express 框架:

const express = require('express');

接下來,我們要實例化 Express 應用:

const app = express();

並設置我們的端口:

const port = 3000;

稍後,當我們告訴應用程序偵聽請求時,將使用該端口。這三行是樣板文件 - 但最棒的是,這就是所有樣板文件!

現在,我們可以創建一個簡單的 GET 樣板正下方的端點。當用戶使用 GET 請求到達端點時,消息“Hello World, from express” 將被返回(並在瀏覽器中呈現或顯示在控制台上)。

我們想把它設置在主頁上,所以端點的 URL 是 /

app.get('/', (req, res) => {
    res.send('Hello World, from express');
});

此時,讓我們開始我們的客戶端:

app.listen(port, () => console.log(`Hello world app listening on port ${port}!`))

讓我們運行應用程序並通過瀏覽器訪問我們擁有的唯一端點:

$ node hello-world.js
Hello world app listening on port 3000!

從技術上講,這是一個有效的 API!雖然,這個端點並沒有真正做太多。讓我們看看一些常見的中間件,它們對進一步的工作很有用,並創建一些更有用的端點。

快遞中間件

如上所述 - ExpressJS 是一個簡單的 HTTP 服務器,它沒有很多開箱即用的功能。中間件的作用幾乎類似於 Express 服務器的擴展,並在請求的“中間”提供額外的功能。許多第三方擴展,例如用於日誌記錄的 morgan、用於處理文件上傳的 multer,都被常規使用。

現在,要開始使用,我們需要安裝一個名為 body-parser 的中間件,它可以幫助我們從 HTTP 請求中解碼正文:

$ npm install --save body-parser

它解析請求的主體,並讓我們做出相應的反應。

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

因為我們通過點擊瀏覽器中的端點從不同的位置調用 API。我們還必須安裝 CORS 中間件。

如果你還不熟悉跨域資源共享,暫時還可以。讓我們安裝中間件並配置它:

$ npm install --save cors

使用 Node 和 Express 構建 REST API

添加書籍

現在我們可以開始構建我們的應用程序了。創建一個名為 book-api.js 的新文件 :

const express = require('express')
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();
const port = 3000;

// Where we will keep books
let books = [];

app.use(cors());

// Configuring body parser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.post('/book', (req, res) => {
    // We will be coding here
});

app.listen(port, () => console.log(`Hello world app listening on port ${port}!`));

可以看到,我們可以配置body-parser 通過導入它並將其傳遞給 app.use 方法,使其成為 Express app 的中間件 實例。

我們將使用 books 數組來存儲我們的書籍集合,模擬一個數據庫。

HTTP 請求正文類型有幾種類型。例如,application/x-www-form-urlencoded 是表單的默認正文類型,而 application/json 是我們在使用 jQuery 或後端 REST 客戶端請求資源時使用的東西。

body-parser 是什麼 中間件將做的是抓取 HTTP 正文,解碼信息,並將其附加到 req.body .從那裡,我們可以輕鬆地從表單中檢索信息——在我們的例子中,是一本書的信息。

app.post 內部 方法讓我們將這本書添加到書籍數組中:

app.post('/book', (req, res) => {
    const book = req.body;

    // Output the book to the console for debugging
    console.log(book);
    books.push(book);

    res.send('Book is added to the database');
});

現在,讓我們創建一個簡單的 HTML 表單,其中包含以下字段:ISBN、標題、作者、發布日期、出版商和新文件中的頁數,比如 new-book.html .

我們將使用此 HTML 表單的 action 將數據發送到 API 屬性:

<div class="container">
    <hr>
    <h1>Create New Book</h1>
    <hr>

    <form action="http://localhost:3000/book" method="POST">
        <div class="form-group">
            <label for="ISBN">ISBN</label>
            <input class="form-control" name="isbn">
        </div>

        <div class="form-group">
            <label for="Title">Title</label>
            <input class="form-control" name="title">
        </div>

        <!--Other fields-->
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
</div>

在這裡,我們的 <form> 標籤的屬性對應於我們的端點和我們使用 submit 發送的信息 button 是我們的方法解析並添加到數組中的信息。請注意,method 參數是 POST ,就像在我們的 API 中一樣。

打開頁面時應該會看到類似的內容:

點擊“提交”,我們的應用程序console.log(book) 聲明:

{ isbn: '9781593275846',
  title: 'Eloquent JavaScript, Second Edition',
  author: 'Marijn Haverbeke',
  publish_date: '2014-12-14',
  publisher: 'No Starch Press',
  numOfPages: '472' }

注意: 請注意,由於我們使用數組來存儲數據,我們將在下次應用重啟時丟失它們。

獲取所有書籍

現在讓我們創建一個端點來從 API 中獲取所有書籍:

app.get('/books', (req, res) => {
    res.json(books);
});

重新啟動服務器。如果服務器已經在運行,請按 Ctrl + C 首先阻止它。添加一些書籍並打開 http://localhost:3000/books 在您的瀏覽器中。您應該會看到包含您添加的所有書籍的 JSON 響應。

現在讓我們創建一個 HTML 頁面,以用戶友好的方式顯示這些書籍。

這一次,我們將創建兩個文件 - book-list.html 我們將使用它作為模板和 book-list.js 該文件將保存更新/刪除書籍並在頁面上顯示它們的邏輯:

讓我們從模板開始:

<div class="container">
    <hr>
    <h1>List of books</h1>
    <hr>
    <div>
        <div class="row" id="books">
        </div>
    </div>
</div>

<div id="editBookModal" class="modal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Edit Book</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>

            <div class="modal-body">
                <form id="editForm" method="POST">
                    <div class="form-group">
                        <label for="ISBN">ISBN</label>
                        <input class="form-control" name="isbn" id="isbn">
                    </div>

                    <div class="form-group">
                        <label for="Title">Title</label>
                        <input class="form-control" name="title" id="title">
                    </div>

                    <!--Other fields-->

                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
            </div>
        </div>
    </div>
</div>
<!--Our JS file-->
<script src="book-list.js"></script>

完成模板後,我們可以使用瀏覽器端 JavaScript 和我們的 REST API 來實現檢索所有書籍的實際邏輯:

const setEditModal = (isbn) => {
    // We will implement this later
}

const deleteBook = (isbn) => {
    // We will implement this later
}

const loadBooks = () => {
    const xhttp = new XMLHttpRequest();

    xhttp.open("GET", "http://localhost:3000/books", false);
    xhttp.send();

    const books = JSON.parse(xhttp.responseText);

    for (let book of books) {
        const x = `
            <div class="col-4">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">${book.title}</h5>
                        <h6 class="card-subtitle mb-2 text-muted">${book.isbn}</h6>

                        <div>Author: ${book.author}</div>
                        <div>Publisher: ${book.publisher}</div>
                        <div>Number Of Pages: ${book.numOfPages}</div>

                        <hr>

                        <button type="button" class="btn btn-danger">Delete</button>
                        <button types="button" class="btn btn-primary" data-toggle="modal"
                            data-target="#editBookModal" onClick="setEditModal(${book.isbn})">
                            Edit
                        </button>
                    </div>
                </div>
            </div>
        `

        document.getElementById('books').innerHTML = document.getElementById('books').innerHTML + x;
    }
}

loadBooks();

在上面的腳本中,我們發送一個 GET 對端點 http://localhost:3000/books 的請求 檢索書籍,然後為每本書創建一個引導卡以顯示它。如果一切正常,您應該會在頁面上看到類似這樣的內容:

你可能注意到了編輯創建 按鈕及其各自的方法。現在,讓我們將它們留空並在執行過程中實現它們。

按 ISBN 檢索圖書

如果我們想向用戶顯示一本特定的書,我們需要一種從數據庫(或者在我們的例子中是數組)中檢索它的方法。這始終由特定於該實體的密鑰完成。在大多數情況下,每個實體都有一個唯一的 id 這有助於我們識別它們。

在我們的例子中,每本書都有一個 ISBN,它本質上是唯一的,所以不需要另一個 id 價值。

這通常通過解析 id 的 URL 參數來完成 並使用相應的 id 搜索圖書 .

例如,如果 ISBN 是 9781593275846 URL 看起來像 http://localhost:3000/book/9781593275846

app.get('/book/:isbn', (req, res) => {
    // Reading isbn from the URL
    const isbn = req.params.isbn;
});

在這裡,我們介紹了參數化的 URL。由於 ISBN 取決於書籍,因此這裡可能存在無限數量的端點。通過添加冒號 (: ) 到路徑,我們可以定義一個變量,映射到變量isbn .所以,如果用戶訪問 localhost:3000/book/5 isbn 參數將是 5 .

如果在您的場景中有意義,您可以在 URL 中接受多個參數。例如 /image/:width/:height ,然後您可以使用 req.params.width 獲取這些參數 和 req.params.height .

現在,使用我們的端點,我們可以檢索一本書:

app.get('/book/:isbn', (req, res) => {
    // Reading isbn from the URL
    const isbn = req.params.isbn;

    // Searching books for the isbn
    for (let book of books) {
        if (book.isbn === isbn) {
            res.json(book);
            return;
        }
    }

    // Sending 404 when not found something is a good practice
    res.status(404).send('Book not found');
});

再次重啟服務器,添加新書,打開localhost/3000/{your_isbn} 並且應用程序將返回該書的信息。

刪除書籍

刪除實體時,我們一般會一一刪除,以免意外丟失大數據。要刪除項目,我們使用 HTTP DELETE 方法並使用其 ISBN 號指定一本書,就像我們檢索它的方式一樣:

app.delete('/book/:isbn', (req, res) => {
    // Reading isbn from the URL
    const isbn = req.params.isbn;

    // Remove item from the books array
    books = books.filter(i => {
        if (i.isbn !== isbn) {
            return true;
        }
        return false;
    });

    res.send('Book is deleted');
});

我們正在使用 app.delete 接受 DELETE 的方法 要求。我們還使用了數組 filter 方法過濾出具有相關 ISBN 的圖書,將其從數組中刪除。

現在讓我們實現 deleteBook book-list.js 中的方法 文件:

const deleteBook = (isbn) => {
    const xhttp = new XMLHttpRequest();

    xhttp.open("DELETE", `http://localhost:3000/book/${isbn}`, false);
    xhttp.send();

    // Reloading the page
    location.reload();
}

在這個方法中,我們在按下按鈕時發送刪除請求並重新加載頁面以顯示更改。

編輯書籍

與刪除實體非常相似,更新它們需要我們根據 ISBN 抓取一個特定的實體,然後發送 POSTPUT 帶有新信息的 HTTP 調用。

讓我們回到我們的 book-api.js 文件:

app.post('/book/:isbn', (req, res) => {
    // Reading isbn from the URL
    const isbn = req.params.isbn;
    const newBook = req.body;

    // Remove item from the books array
    for (let i = 0; i < books.length; i++) {
        let book = books[i]
        if (book.isbn === isbn) {
            books[i] = newBook;
        }
    }

    res.send('Book is edited');
});

在發送 POST 請求,針對特定的 ISBN,用新信息更新適當的書籍。

由於我們已經創建了編輯模式,我們可以使用 setEditModal 單擊“編輯”按鈕時收集有關圖書信息的方法。

我們還將設置表單的 action 帶有被點擊書的 URL 的參數來發送請求:

const setEditModal = (isbn) => {
    // Get information about the book using isbn
    const xhttp = new XMLHttpRequest();

    xhttp.open("GET", `http://localhost:3000/book/${isbn}`, false);
    xhttp.send();

    const book = JSON.parse(xhttp.responseText);

    const {
        title,
        author,
        publisher,
        publish_date,
        numOfPages
    } = book;

    // Filling information about the book in the form inside the modal
    document.getElementById('isbn').value = isbn;
    document.getElementById('title').value = title;
    document.getElementById('author').value = author;
    document.getElementById('publisher').value = publisher;
    document.getElementById('publish_date').value = publish_date;
    document.getElementById('numOfPages').value = numOfPages;

    // Setting up the action url for the book
    document.getElementById('editForm').action = `http://localhost:3000/book/${isbn}`;
}

要驗證更新功能是否有效,請編輯一本書。表格應填寫有關該書的現有信息。更改一些內容並單擊“提交”,之後您應該會看到“圖書已編輯”消息。

結論

這就是使用 Node.js 和 Express 構建 REST API 的容易程度。如果您有興趣,可以訪問官方 Express 文檔以了解有關該框架的更多信息。

另外,我提供的代碼只是為了教程,你不應該在生產環境中使用它。確保在為生產編寫代碼時驗證數據並遵循最佳實踐。

和往常一樣,這個項目的源代碼可以在 GitHub 上找到。


Tutorial JavaScript 教程
  1. 使用 JavaScript 獲取下一個/上一個元素?

  2. 高階 JS 函數

  3. 如何在 JavaScript 中深度克隆數組

  4. 使用 ReacType 更聰明地工作,而不是更難

  5. 什麼是 JavaScript 中的對象解構?

  6. Axios 實例無法與 Nextjs 一起使用:“未定義本地存儲”

  7. 我為什麼要學習 ReactJS?

  1. NPM:如何修補依賴項📦

  2. 如何在 Fastify 上使用 Schema 來獲得樂趣和利潤

  3. 返回 if 語句的變量 javascript

  4. 如何在幾秒鐘內在網站中添加加載器😎

  5. 刮NBA p1-球員/球隊

  6. 反應井字遊戲

  7. 網站如何運作? 👩🏻‍💻 簡單舉例說明

  1. 探索 NestJS 工作區

  2. Elm 與 Vue

  3. 如何在 30 分鐘內用 React Native 構建一個 Geolocation Weather Forecast 應用程序

  4. 我試圖用 30 個字用 Javascript 解釋一切。