日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
使用 Node.js 構(gòu)建博客 API

介紹

在本文中,我們將使用 Node.js 構(gòu)建一個(gè)簡單的博客 API。 API代表“應(yīng)用程序編程接口”,它允許不同的軟件系統(tǒng)相互通信。 在這種情況下,我們的博客 API 將允許我們創(chuàng)建、讀取、更新和刪除博客文章,以及管理用戶身份驗(yàn)證。

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的南昌縣網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

為什么要使用 Node.js 構(gòu)建博客 API? Node.js 是一種流行的開源運(yùn)行時(shí)環(huán)境,用于在瀏覽器外部執(zhí)行 JavaScript 代碼。 它擁有龐大而活躍的開發(fā)人員社區(qū)以及豐富的庫和框架,可以輕松構(gòu)建可擴(kuò)展的高性能 Web 應(yīng)用程序。

先決條件

以下是我們需要的:

  • Node.js: 您可以從官方網(wǎng)站下載最新版本的Node.js,并按照安裝說明操作。 此操作還將安裝 Node.js 包管理器 (npm),我們將使用它來安裝其他包。
  • MongoDB: 我們將使用 MongoDB 數(shù)據(jù)庫來存儲(chǔ)數(shù)據(jù)。 您可以按照官方網(wǎng)站上的安裝說明進(jìn)行操作。
  • 代碼編輯器:您可以使用您喜歡的任何代碼編輯器,例如 Visual Studio Code、Sublime Text 或 Atom。
  • 終端:我們將使用集成在 VS Code中的終端來運(yùn)行命令并與項(xiàng)目交互,但您可以使用您喜歡的終端。
  • 基本了解 JavaScript、API、數(shù)據(jù)庫和其他 Web 開發(fā)概念。 如果您是這些主題的新手,那么在深入學(xué)習(xí)本教程之前復(fù)習(xí)一下您的知識(shí)可能會(huì)有所幫助。

設(shè)置開發(fā)環(huán)境

創(chuàng)建一個(gè)新項(xiàng)目

  • 要?jiǎng)?chuàng)建一個(gè)新項(xiàng)目,請打開您的終端并導(dǎo)航到您要?jiǎng)?chuàng)建項(xiàng)目的目錄。 然后,運(yùn)行以下命令:
npm init -y
  • 這將在您的項(xiàng)目目錄中創(chuàng)建一個(gè) package.json 文件,用于管理項(xiàng)目的依賴項(xiàng)和腳本。 -y 表示接受所有默認(rèn)選項(xiàng),因此您不必手動(dòng)輸入選項(xiàng)。
  • 接下來,運(yùn)行以下命令來安裝這些依賴項(xiàng):
npm install --save express dotenv cors express-rate-limit helmet express-fileupload

文件結(jié)構(gòu)

現(xiàn)在我們已經(jīng)安裝了依賴項(xiàng),讓我們?yōu)轫?xiàng)目創(chuàng)建文件結(jié)構(gòu)。 在項(xiàng)目目錄中創(chuàng)建以下目錄和文件:

  • /src: 這是我們項(xiàng)目的主目錄。 它將包含我們應(yīng)用程序的所有源代碼。
  • /src/authentication: 該目錄將包含與用戶身份驗(yàn)證相關(guān)的代碼,例如密碼哈希加密和 JWT 生成。
  • /src/config: 該目錄將包含我們應(yīng)用程序的配置文件,例如數(shù)據(jù)庫連接字符串和服務(wù)器端口號。
  • /src/controllers: 該目錄將包含 API 控制器的代碼。 控制器處理傳入的請求并向客戶端發(fā)送響應(yīng)。
  • /src/database: 此目錄將包含用于連接到數(shù)據(jù)庫并與之交互的代碼。
  • /src/loggers: 該目錄將包含用于在我們的應(yīng)用程序中記錄日志的代碼。
  • /src/models:該目錄將包含我們應(yīng)用程序的數(shù)據(jù)庫模型的代碼。 模型代表存儲(chǔ)在數(shù)據(jù)庫中的數(shù)據(jù),并提供與該數(shù)據(jù)交互的接口。
  • /src/routes:該目錄將包含 API 路由的代碼。 路由定義我們 API 的端點(diǎn)(endpoint)并指定可用于訪問它們的 HTTP 方法(例如 GET、POST)。
  • /src/services: 該目錄將包含在整個(gè)應(yīng)用程序中使用的服務(wù)對象或?qū)嵱贸绦蚝瘮?shù)。
  • /src/validators: 該目錄將包含用于驗(yàn)證輸入數(shù)據(jù)的代碼。
  • app.js: 該文件將包含服務(wù)器的中間件和路由器。
  • index.js: 這是我們應(yīng)用程序的入口點(diǎn)。 它將包含用于啟動(dòng)服務(wù)器和連接到數(shù)據(jù)庫的代碼。
  • package.json: 此文件包含 API 的元數(shù)據(jù)。 啟動(dòng)腳本應(yīng)該是:“start”:“node index.js”。

設(shè)置環(huán)境變量

讓我們設(shè)置項(xiàng)目所需的環(huán)境變量。 隨著我們的構(gòu)建逐漸完成,為什么需要每個(gè)環(huán)境變量會(huì)變得逐漸清晰。

避免暴露 API ,因?yàn)檫@存在安全風(fēng)險(xiǎn)。 如果你使用版本控制,你可以設(shè)置一個(gè)配置文件,可以在不暴露任何秘密的情況下推送,或者你可以創(chuàng)建一個(gè) .env.example 只顯示變量名,沒有值。

在 /src/config 中創(chuàng)建 index.js 文件:

require("dotenv").config();

module.exports = {
PORT: process.env.PORT,
MONGODB_CONNECTION_URL: process.env.MONGODB_CONNECTION_URL,
JWT_SECRET: process.env.JWT_SECRET,
CLOUDINARY_URL: process.env.CLOUDINARY_URL,
};

在根目錄中,創(chuàng)建一個(gè) .env 文件。 我們將在構(gòu)建過程中更新這些值:

PORT=5000
MONGODB_CONNECTION_URL=
JWT_SECRET=
CLOUDINARY_URL=

設(shè)置服務(wù)器

  1. 在 app.js 文件中,添加以下代碼:
const express = require("express");
const rateLimit = require("express-rate-limit");
const helmet = require("helmet");
const cors = require("cors");

const CONFIG = require("./src/config");

const app = express();

app.use(cors());

const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: true,
message: "請求過多,請15分鐘后重試",
});
app.use(limiter);
app.use(helmet());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
fileUpload({
createParentPath: true,
useTempFiles: true,
tempFileDir: "/tmp/",
})
);

app.get("/", (req, res) => {
return res.json({ status: true });
});

// 404 error處理
app.use("*", (req, res) => {
return res.status(404).json({ message: "路由未找到" });
});

// Error處理
app.use(function (err, req, res, next) {
console.log(err.message);
res.status(err.status || 500).send("發(fā)生錯(cuò)誤");
});

module.exports = app;

我們設(shè)置了以下中間件:

cors 允許跨源資源共享,這意味著我們的 API 可以從不同的域訪問。

代碼中添加了限速中間件,可以限制用戶在一定時(shí)間內(nèi)允許發(fā)出的請求數(shù)量。 這有助于防止惡意用戶試圖用暴力請求攻擊服務(wù)器:

const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: true,
message: ""請求過多,請15分鐘后重試",
});
app.use(limiter);

我們還有helmet中間件,它通過設(shè)置各種 HTTP 標(biāo)頭來幫助保護(hù) API。

然后,是以下中間件,它允許 API 處理請求正文中的 JSON 和 URL 編碼數(shù)據(jù):

app.use(express.json()); 
app.use(express.urlencoded({ extended: true }));

最后,我們有 fileUpload 中間件,它允許 API 處理文件上傳:

app.use(fileUpload({ 
createParentPath: true,
useTempFiles: true,
tempFileDir: "/tmp/",
}));
  1. 在index.js 文件中, 添加以下代碼:
const app = require("./app");

const CONFIG = require("./src/config");

app.listen(CONFIG.PORT, () {
console.log(`server listening on port ${CONFIG.PORT}`);
});

前面我們設(shè)置了開發(fā)環(huán)境和基本文件結(jié)構(gòu)。 現(xiàn)在,讓我們設(shè)計(jì) API 本身。 在節(jié)中,我們將討論以下主題:

  • 決定 API 的路由和功能
  • 為數(shù)據(jù)庫定義數(shù)據(jù)模型
  • 實(shí)現(xiàn)數(shù)據(jù)模型
  • 設(shè)置數(shù)據(jù)庫連接

決定路由和功能

設(shè)計(jì)API 的第一步是決定要包含的路由和功能。

概述一下我們博客的要求:

  • 用戶應(yīng)該能夠注冊并登錄博客應(yīng)用程序。
  • 博客文章可以處于兩種狀態(tài):草稿和已發(fā)布。
  • 用戶能夠獲得已發(fā)表文章的列表,無論他是否登錄。
  • 用戶應(yīng)該能夠獲得已發(fā)表的文章,無論他是否登錄。
  • 登錄用戶應(yīng)該能夠創(chuàng)建文章。
  • 創(chuàng)建文章時(shí),它應(yīng)該處于草稿狀態(tài)。
  • 文章的作者應(yīng)該能夠?qū)⑽恼碌臓顟B(tài)更新為已發(fā)布。
  • 文章的作者應(yīng)該能夠編輯處于草稿或已發(fā)布狀態(tài)的文章。
  • 文章的作者應(yīng)該能夠刪除處于草稿或已發(fā)布狀態(tài)的文章。
  • 文章的作者應(yīng)該能夠得到他們文章的列表。結(jié)果內(nèi)容應(yīng)該按狀態(tài)分頁和過濾。
  • 創(chuàng)建的文章應(yīng)該有標(biāo)題、封面圖片、描述、標(biāo)簽、作者、時(shí)間戳、狀態(tài)、閱讀次數(shù)、閱讀時(shí)間和正文。
  • 登錄和未登錄用戶都可以訪問的文章列表端點(diǎn),內(nèi)容應(yīng)該分頁。
  • 博客文章應(yīng)該可以按作者、標(biāo)題和標(biāo)簽進(jìn)行搜索。
  • 博客文章還應(yīng)該可以按閱讀次數(shù)、閱讀時(shí)間和時(shí)間戳排序
  • 請求單篇文章時(shí),API應(yīng)隨博客文章同時(shí)返回作者信息,博客閱讀次數(shù)加1。

考慮到上述要求,我們將定義路由如下:

博客路由:

  • GET /blog: 檢索所有已發(fā)表文章的列表。
  • GET /blog/:article_id:通過 ID 檢索單篇文章。

作者路由: 我們希望只有經(jīng)過身份驗(yàn)證的用戶才能訪問這些路由和所有 CRUD 操作。

  • GET /author/blog: 檢索用戶創(chuàng)建的所有已發(fā)布文章的列表。
  • POST /author/blog: 創(chuàng)建新文章。
  • PATCH /author/blog/edit/:article_id: 通過 ID 更新文章。
  • PATCH /author/blog/edit/state/:article_id: 更新文章的狀態(tài)。
  • DELETE /author/blog/:article_id: 按 ID 刪除文章。

認(rèn)證路由: 用于管理用戶身份驗(yàn)證。

  • POST /auth/signup: 注冊一個(gè)新用戶。
  • POST /auth/login: 登錄現(xiàn)有用戶。

定義數(shù)據(jù)模型

定義好路由后,我們就可以開始考慮數(shù)據(jù)庫的數(shù)據(jù)模型了。 數(shù)據(jù)模型表示將存儲(chǔ)在數(shù)據(jù)庫中的數(shù)據(jù)以及這些數(shù)據(jù)之間的關(guān)系。 我們將使用 Mongoose 來定義我們的架構(gòu)。

我們將有兩個(gè)數(shù)據(jù)模型:Blog和User。

User

字段名

數(shù)據(jù)類型

約束

firstname

String

required

lastname

String

required

email

String

required, unique, index

password

String

required

articles

Array, [ObjectId]

ref - Blog

Blog

字段名

數(shù)據(jù)類型

約束

title

String

required, unique, index

description

String

tags

Array, [String]

imageUrl

String

author

ObjectId

ref - Users

timestamp

Date

state

String

required, enum: ['draft', 'published'], default:'draft'

readCount

Number

default:0

readingTime

String

body

String

required

Mongoose 有一個(gè)名為 populate() 的方法,它允許您引用其他集合中的文檔。 populate() 將自動(dòng)用其他集合中的文檔替換文檔中的指定路徑。 User 模型將其 articles 字段設(shè)置為 ObjectId 的數(shù)組。 ref 選項(xiàng)告訴 Mongoose 在填充期間使用哪個(gè)模型,在本例中為Blog模型。 我們在這里存儲(chǔ)的所有 _id 必須是博客模型中的文章 _id。 同樣,Blog 模型在其author字段中引用了 User 模型。

實(shí)現(xiàn)數(shù)據(jù)模型

  • 在 /src/models目錄下, 創(chuàng)建一個(gè)名為 blog.model.js 的文件并設(shè)置 Blog 模型:
const mongoose = require("mongoose");
const uniqueValidator = require('mongoose-unique-validator');

const { Schema } = mongoose;

const BlogSchema = new Schema({
title: { type: String, required: true, unique: true, index: true },
description: String,
tags: [String],
author: { type: Schema.Types.ObjectId, ref: "Users" },
timestamp: Date,
imageUrl: String,
state: { type: String, enum: ["draft", "published"], default: "draft" },
readCount: { type: Number, default: 0 },
readingTime: String,
body: { type: String, required: true },
});

// 將 uniqueValidator 插件應(yīng)用于blog模型
BlogSchema.plugin(uniqueValidator);

const Blog = mongoose.model("Blog", BlogSchema);

module.exports = Blog;

title字段被定義為必填的字符串,并且在集合中的所有文檔中必須是唯一的。 description 字段定義為字符串,tags 字段定義為字符串?dāng)?shù)組。 author字段定義為對用戶集合中文檔的引用,timestamp字段定義為日期。 imageUrl 字段定義為字符串,state 字段定義為具有一組允許值(“draft”或“published”)的字符串,readCount 字段定義為默認(rèn)值為 0 的數(shù)字。 readingTime 字段定義為字符串,body 字段定義為必填字符串。

mongoose-unique-validator 是一個(gè)插件,它為 Mongoose 模式中的唯一字段添加預(yù)保存驗(yàn)證。 如果唯一字段的值已存在于集合中,它將驗(yàn)證模型中的唯一選項(xiàng),并阻止插入文檔。

  • 在 /src/models目錄下, 創(chuàng)建一個(gè)名為 user.model.js 的文件并設(shè)置User模型:
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
const bcrypt = require("bcrypt");

const { Schema } = mongoose;

const UserModel = new Schema({
firstname: { type: String, required: true },
lastname: { type: String, required: true },
email: {
type: String,
required: true,
unique: true,
index: true,
},
password: { type: String, required: true },
articles: [{ type: Schema.Types.ObjectId, ref: "Blog" }],
});

// 將 uniqueValidator 插件應(yīng)用于用戶模型
UserModel.plugin(uniqueValidator);

UserModel.pre("save", async function (next) {
const user = this;

if (user.isModified("password") || user.isNew) {
const hash = await bcrypt.hash(this.password, 10);

this.password = hash;
} else {
return next();
}
});

const User = mongoose.model("Users", UserModel);

module.exports = User;

firstname和lastname字段被定義為必填字符串,email字段被定義為必填字符串,并且在集合中的所有文檔中必須是唯一的。 password 字段定義為必填字符串,articles 字段定義為對 Blog 集合中文檔的引用數(shù)組。

pre函數(shù)在特定方法執(zhí)行前執(zhí)行特定代碼。 在將用戶文檔保存到數(shù)據(jù)庫之前,此處的預(yù)保存函數(shù)使用 npm 模塊 bcrypt 對用戶密碼進(jìn)行哈希加密處理。

設(shè)置數(shù)據(jù)庫連接

現(xiàn)在我們已經(jīng)定義了路由和數(shù)據(jù)模型,是時(shí)候設(shè)置數(shù)據(jù)庫連接了。

  • 設(shè)置您的 MongoDB 數(shù)據(jù)庫并將連接 url 保存在您的 .env 文件中。
  • 運(yùn)行以下命令安裝 npm 包 mongoose:
npm install --save mongoose
  • 在 /database 目錄中創(chuàng)建一個(gè)名為 db.js 的文件。 在 /database/db.js 中,使用 Mongoose 設(shè)置數(shù)據(jù)庫連接:
const mongoose = require('mongoose');

const connect = (url) {
mongoose.connect(url || 'mongodb://[localhost:27017](http://localhost:27017)')

mongoose.connection.on("connected", () {
console.log("Connected to MongoDB Successfully");
});

mongoose.connection.on("error", (err) {
console.log("An error occurred while connecting to MongoDB");
console.log(err);
});
}

module.exports = { connect };

connect 函數(shù)有一個(gè)可選的 url 參數(shù),它指定要連接的數(shù)據(jù)庫的 URL。 如果未提供 URL,則默認(rèn)為“mongodb://localhost:27017”,這將連接到本機(jī)運(yùn)行的MongoDB數(shù)據(jù)庫實(shí)例的默認(rèn)端口 (27017) 。

  • 在 /database 目錄下創(chuàng)建一個(gè) index.js 文件:
const database = require("./db");

module.exports = {
database,
};

現(xiàn)在我們已經(jīng)建立了數(shù)據(jù)庫連接,在下文中,我們將深入探討兩個(gè)重要的概念——身份驗(yàn)證和數(shù)據(jù)驗(yàn)證。

讓我們從安裝所需的依賴項(xiàng)開始:

npm install joi jsonwebtoken passport passport-jwt passport-local passport-local-mongoose

使用 Passport.js 進(jìn)行身份驗(yàn)證

為了在我們的 API 中對用戶進(jìn)行身份驗(yàn)證,我們將使用流行的 Passport.js 庫。 當(dāng)用戶登錄時(shí),我們將為用戶生成一個(gè) JWT 令牌。 該令牌將用于授權(quán)用戶對 API 的請求。

在/authentication/passport.js 文件中,設(shè)置 JWT 策略:

   const passport = require("passport");
const localStrategy = require("passport-local").Strategy;
const { UserModel } = require("../models");

const JWTstrategy = require("passport-jwt").Strategy;
const ExtractJWT = require("passport-jwt").ExtractJwt;

passport.use(
new JWTstrategy(
{
secretOrKey: process.env.JWT_SECRET,
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
},
async (token, done) => {
try {
return done(null, token.user);
} catch (error) {
done(error);
}
}
)
);

JSON Web Token (JWT) 策略允許我們通過驗(yàn)證用戶的 JWT 來對用戶進(jìn)行身份驗(yàn)證。 要使用此策略,我們傳入一個(gè)密鑰(我們已將其存儲(chǔ)在 .env 文件中)和一個(gè)函數(shù)。
ExtractJWT.fromAuthHeaderAsBearerToken() 方法將從請求的授權(quán)標(biāo)頭中提取 JWT 作為 Bearer 令牌。 如果找到 JWT,它將被解碼并與 done 函數(shù)一起傳遞給該函數(shù)。 該函數(shù)將檢查 JWT 中的用戶對象以查看其是否有效。 如果有效,則調(diào)用帶有用戶對象的 done 函數(shù),表明用戶已通過身份驗(yàn)證,否則將調(diào)用 done 函數(shù)并出錯(cuò)。

對于 /signup 路由,我們將設(shè)置一個(gè)本地策略,它使用 passport-local 模塊通過用戶名(在本例中為他們的電子郵件地址)和密碼對用戶進(jìn)行身份驗(yàn)證。 當(dāng)用戶注冊時(shí),我們將在數(shù)據(jù)庫中創(chuàng)建一個(gè)新的用戶文檔并將其返回給 Passport。

   passport.use(
"signup",
new localStrategy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},
async (req, email, password, done) => {
try {

const user = await UserModel.create({ ...req.body, password });

return done(null, user);
} catch (error) {
done(error);
}
}
)
);
  • 登錄策略也將使用 passport-local 模塊。 當(dāng)用戶登錄時(shí),我們會(huì)在數(shù)據(jù)庫中搜索匹配的電子郵件地址,并驗(yàn)證該用戶是否存在于數(shù)據(jù)庫中。 如果登錄成功,我們將用戶對象返回給 Passport。
   passport.use(
"login",
new localStrategy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},
async (req, email, password, done) => {
try {
const user = await UserModel.findOne({ email });

if (!user) {
return done(null, false, { message: "User not found" });
}

return done(null, user, { message: "Logged in Successfully" });
} catch (error) {
return done(error);
}
}
)
);

我們?nèi)匀恍枰?yàn)證用戶的密碼。 在
/src/models/user.models.js 中,在 User 模型中創(chuàng)建一個(gè)名為 isValidPassword() 的方法。

UserModel.methods.isValidPassword = async function (password) {
const user = this;

const match = await bcrypt.compare(password, user.password);

return match;
};

isValidPassword 將密碼作為參數(shù),并使用 bcrypt 的比較方法將其與用戶的散列密碼進(jìn)行比較。 該方法返回一個(gè)布爾值,指示密碼是否匹配。


src/authentication/passport.js中,在登錄策略中調(diào)用isValidPassword驗(yàn)證用戶密碼:

const validate = await user.isValidPassword(password);

if (!validate) {
return done(null, false, {message: "Wrong Password" });
}

完整的文件應(yīng)如下所示:

const passport = require("passport");
const localStrategy = require("passport-local").Strategy;
const { UserModel } = require("../models");

const JWTstrategy = require("passport-jwt").Strategy;
const ExtractJWT = require("passport-jwt").ExtractJwt;

passport.use(
new JWTstrategy(
{
secretOrKey: process.env.JWT_SECRET,
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
},
async (token, done) => {
try {
return done(null, token.user);
} catch (error) {
done(error);
}
}
)
);

passport.use(
"signup",
new localStrategy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},
async (req, email, password, done) => {
try {

const user = await UserModel.create({ ...req.body, password });

return done(null, user);
} catch (error) {
done(error);
}
}
)
);

passport.use(
"login",
new localStrategy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true
},
async (req, email, password, done) => {
try {
const user = await UserModel.findOne({ email });

if (!user) {
return done(null, false, { message: "User not found" });
}

const validate = await user.isValidPassword(password);

if (!validate) {
return done(null, false, { message: "Wrong Password" });
}

return done(null, user, { message: "Logged in Successfully" });
} catch (error) {
return done(error);
}
}
)
);

使用 Joi 驗(yàn)證輸入

為了驗(yàn)證用戶輸入,我們將使用 Joi 庫。 Joi 提供了一種簡單但功能強(qiáng)大的方法來定義和驗(yàn)證 Node.js 應(yīng)用程序中的數(shù)據(jù)結(jié)構(gòu)。

在 /validators 目錄中,創(chuàng)建一個(gè)名為 author.validator.js 的文件:

const Joi = require("joi");

const newArticleValidationSchema = Joi.object({
title: Joi.string().trim().required(),
body: Joi.string().trim().required(),
description: Joi.string().trim(),
tags: Joi.string().trim(),
});

const updateArticleValidationSchema = Joi.object({
title: Joi.string().trim(),
body: Joi.string().trim(),
description: Joi.string().trim(),
tags: Joi.string().trim(),
state: Joi.string().trim(),
});

const newArticleValidationMW = async (req, res, next) => {
const article = req.body;
try {
await newArticleValidationSchema.validateAsync(article);
next();
} catch (error) {
return next({ status: 406, message: error.details[0].message });
}
};

const updateArticleValidationMW = async (req, res, next) => {
const article = req.body;
try {
await updateArticleValidationSchema.validateAsync(article);
next();
} catch (error) {
return next({ status: 406, message: error.details[0].message });
}
};

module.exports = {
newArticleValidationMW,
updateArticleValidationMW,
};

我們導(dǎo)出兩個(gè)中間件函數(shù),newArticleValidationMW 和 updateArticleValidationMW。 newArticleValidationMW 使用
newArticleValidationSchema 來驗(yàn)證新建文章請求的請求正文是否包含所有必填字段(標(biāo)題、正文)以及所有提供的字段是否格式正確。 如果所有字段都有效,它將調(diào)用下一個(gè)函數(shù)以繼續(xù)。 updateArticleValidationMW,與前者類似,但它使用 updateArticleValidationSchema 來驗(yàn)證更新文章請求的請求。

這兩個(gè)函數(shù)都使用 Joi 庫提供的 validateAsync 方法來執(zhí)行驗(yàn)證。 此方法接受一個(gè)對象(請求主體)并返回一個(gè)承諾(promise),如果對象無效則拒絕該對象,如果對象有效則返回該對象。

在 /validators 目錄中,創(chuàng)建一個(gè)名為 user.validator.js 的文件:

const Joi = require("joi");

const validateUserMiddleware = async (req, res, next) => {
const user = req.body;
try {
await userValidator.validateAsync(user);
next();
} catch (error) {
return next({ status: 406, message: error.details[0].message });
}
};

const userValidator = Joi.object({
firstname: Joi.string().min(2).max(30).required(),
lastname: Joi.string().min(2).max(30).required(),
email: Joi.string().email({
minDomainSegments: 2,
tlds: { allow: ["com", "net"] },
}),
password: Joi.string()
.pattern(new RegExp("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})"))
.required(),
});

module.exports = validateUserMiddleware;
  • 在/validators/index.js文件中:
const userValidator = require("./user.validator");
const {
newArticleValidationMW,
updateArticleValidationMW,
} = require("./author.validator");

module.exports = {
userValidator,
newArticleValidationMW,
updateArticleValidationMW,
};

完成身份驗(yàn)證和驗(yàn)證后,我們就可以開始構(gòu)建 API 路由和控制器了。

現(xiàn)在我們已經(jīng)完成了 API 設(shè)計(jì)、模型設(shè)置、數(shù)據(jù)庫連接建立和身份驗(yàn)證策略到位,是時(shí)候開始構(gòu)建實(shí)際的 API 了。

  • 為 API 實(shí)現(xiàn)路由。
  • 編寫控制器來處理請求和發(fā)送響應(yīng)。
  • 使用 Postman 測試 API。

實(shí)現(xiàn)路由

  1. 在 /routes 目錄中創(chuàng)建一個(gè)名為 blog.routes.js 的文件并編寫以下代碼:
const express = require("express");
const { blogController } = require("../controllers");

const blogRouter = express.Router();

blogRouter.get("/", blogController.getPublishedArticles);

blogRouter.get("/:articleId", blogController.getArticle);

module.exports = blogRouter;

express.Router() 函數(shù)創(chuàng)建一個(gè)新的路由對象,可用于定義將用于處理對服務(wù)器的 HTTP 請求的路由。

定義的第一個(gè)路由是路由器根路徑中的 GET 路由。 此路由將處理對服務(wù)器的 HTTP GET 請求,并將使用 blogController 中的 getPublishedArticles 函數(shù)返回所有已發(fā)布文章的列表。

第二個(gè)路由是 GET 路由,在路徑中包含一個(gè)參數(shù) :articleId。 此路由將處理對路徑中具有特定文章 ID 的服務(wù)器的 HTTP GET 請求,并將使用 blogController 中的 getArticle 函數(shù)返回具有指定 ID 的文章。

在 /routes 目錄中創(chuàng)建一個(gè)名為 author.routes.js 的文件并編寫以下代碼:

const express = require("express"); 
const { authorController } = require("../controllers");
const {
newArticleValidationMW,
updateArticleValidationMW,
} = require("../validators");

const authorRouter = express.Router();

// 創(chuàng)建新文章
authorRouter.post("/", newArticleValidationMW, authorController.createArticle);

// 改變狀態(tài)
authorRouter.patch(
"/edit/state/:articleId",
updateArticleValidationMW,
authorController.editState
);

// 編輯文章
authorRouter.patch(
"/edit/:articleId",
updateArticleValidationMW,
authorController.editArticle
);

// 刪除文章
authorRouter.delete("/delete/:articleId", authorController.deleteArticle);

// 根據(jù)創(chuàng)建的作者獲取其創(chuàng)建的文章
authorRouter.get("/", authorController.getArticlesByAuthor);

module.exports = authorRouter;

在這里,
本文題目:使用 Node.js 構(gòu)建博客 API
網(wǎng)站URL:http://m.5511xx.com/article/coecdhp.html