表中的內(nèi)容
- 介紹
- Node.js 對于 Rest API 的重要性!
- 什么是 REST,它如何與 Node.js 融合?在 Node.js 中創(chuàng)建和保護(hù) RESTful API!創(chuàng)建您的第一個應(yīng)用 Express API創(chuàng)建用戶模塊創(chuàng)建身份驗證模塊
- 結(jié)論
一、簡介
應(yīng)用程序編程接口 (API) 的炒作是普遍的。它們使軟件能夠與軟件的內(nèi)部和外部部分進(jìn)行交互,這是可擴展性和可重用性的基本要素。
擁有公共 API 的在線幫助現(xiàn)在非常流行。這些允許其他開發(fā)人員快速結(jié)合社交媒體登錄、信用卡債務(wù)和績效跟蹤等功能。
他們?yōu)榇藢嵺`的標(biāo)準(zhǔn)是指定的 REpresentational State Transfer (REST),它與 ??Node.js 最佳開發(fā)技術(shù)完美配合。此外,您可以閱讀一些關(guān)于 Node.js 開發(fā)最佳實踐的最佳文章。他們可能會提供很大的幫助!
2. Node.js 對于 Rest API 的重要性!
Node.js不是框架或庫,而是由 Chrome 的 V8 JavaScript 引擎提供支持的運行時上下文。
作為開源項目,Node.js 由云計算和 Node.js 最佳開發(fā)提供商 Joyent 贊助。該公司資助了其他幾項技術(shù),如 Ruby on Rails 框架,并為 Twitter 和 LinkedIn 實施了托管職責(zé)。
LinkedIn 也成為首批使用 Node.js 為其移動應(yīng)用程序后端創(chuàng)建新項目的公司之一。該技術(shù)隨后被 Uber、eBay 和 Netflix 等許多技術(shù)管理員選中。
不過,直到后來,Node.js 服務(wù)器才開始廣泛使用服務(wù)器端 JavaScript。這項技術(shù)的投資在 2017 年達(dá)到頂峰,并且仍處于領(lǐng)先地位。
Node.js IDEs 是最流行的代碼編輯器,它有 JavaScript 和 Node.js 的幫助和插件,所以它只是意味著你如何根據(jù)編碼要求自定義 IDE。但是,許多 Node.js 開發(fā)人員稱贊來自 VS Code、Brackets 和 WebStorm 的特定工具。
在簡單的 Node.js 最佳開發(fā)中使用中間件是一種讓開發(fā)人員的生活更舒適的通用方法。然而,Node.js 一直是許多開發(fā)人員創(chuàng)建新的 Restful API 的最可靠來源之一。
Node.js 的力量和趨勢使它成為一場激烈的辯論。但是,您可以通過學(xué)習(xí)和探索更多 Node.js Rest API 來決定。
3. 什么是 REST 以及它如何與 Node.js 融合?
REST 是 REST API 的設(shè)計模型或設(shè)計風(fēng)格。RESTful Web 應(yīng)用程序的使用因?qū)⑵渲R呈現(xiàn)為與其支持相關(guān)的數(shù)據(jù)形式而受到贊賞。
使用 Node.js 的 REST API 還有助于其客戶在設(shè)備上執(zhí)行操作,例如替換當(dāng)前資源或設(shè)計不同的資源。
為了保護(hù)您的 RESTful API,您必須開發(fā)各種約束,而 Node.js 非常適合這一點。Node.js 服務(wù)器將設(shè)置 REST 的一組限制,以使 API 易于實踐和創(chuàng)建。
它表明,剛開始管理您的 API 的 Nodejs 開發(fā)人員將高效快速地學(xué)習(xí)它。
此外,每當(dāng)請求使用 RESTful API 時,Node.js 服務(wù)器都會將所請求資源的狀態(tài)表示分配給客戶。
3.1 在 Node.js 中創(chuàng)建和保護(hù) RESTful API!
正如您知道需要創(chuàng)建什么以及要求是什么一樣,這是開始創(chuàng)建應(yīng)用程序的機會。
首先,啟動一個終端,將其轉(zhuǎn)移到您通常創(chuàng)建項目的記錄,并在那里建立一個新目錄:
mkdir express-ads-api
接下來,進(jìn)入這個全新的目錄并練習(xí) npm install 來構(gòu)建一個新項目:
npm init -y
命令 over 將使用任何想要的屬性來構(gòu)建項目。如果您在文本目錄或 IDE 中啟動此目錄,您會注意到您啟動的 npm 命令形成了一個名為 package.json 的文件。違反此文件,您會發(fā)現(xiàn)如下內(nèi)容:
JSON:
{
"name": "express-ads-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
在這一點上,這些數(shù)據(jù)很短,沒有那么多引人入勝的數(shù)據(jù)。然而,當(dāng)您開始計算項目的任務(wù)時,傾向于該文件將開始并變得更加令人印象深刻。
隨后,您將在設(shè)計源中建立一個名為 src 的新目錄:
mkdir src
此處的目的是將所有參考代碼放在此記錄中。因此,創(chuàng)建此目錄,在其中構(gòu)建一個名為 index.js 的不同文件,并將生成的代碼附加到其中:
// ./src/index.js
console.log('Good Morning!');
保留此文件后,您可以將其定向回您的計算機并發(fā)出以下命令來試驗它:
node src
如果這按預(yù)期運行,您會注意到“早上好!” 在您的屏幕上縮進(jìn)。
區(qū)域性工作的 Node.js 應(yīng)用程序“早安”console.log 信息.
3.2 創(chuàng)建你的第一個 App Express API
現(xiàn)在,您設(shè)計的項目只記錄了一條潛在消息。由于這可能不是很有價值,在創(chuàng)建您的“早安!”之后。與 Node.js 一起使用,您可以開始專注于創(chuàng)建RESTful API。
為此,您首先需要在某些省份進(jìn)行投資。因此,直接到您的計算機并宣布以下命令:
npm install body-parser cors express helmet morgan
此命令將在您的設(shè)計中建立五個依賴項:
- body-parser:您將練習(xí)此依賴項以將傳入應(yīng)用程序的基礎(chǔ)轉(zhuǎn)換為 JavaScript 對象。
- cors:使用此依賴項來配置 Express 以組合標(biāo)頭,聲明您的 Rest API 允許來自其他來源的請求。這被視為跨域資源共享 (CORS)。
- express: Express 庫。
- morgan:這個庫為您的 Express Rest API 提供了一些日志記錄功能。
在開始之前的命令后,您將在您的項目中標(biāo)記兩個項目。首先,package.json 文件將包含一個稱為依賴項的原始功能,以及之前的所有庫。
這就是 NPM 確定項目需要哪些依賴項的方式。其次,您將在項目根目錄中看到一個名為 package-lock.json 的不同文件。
NPM 安裝此文件以識別您在開發(fā)時練習(xí)的特定庫,因此它始終應(yīng)用相同的庫。
當(dāng) NPM 終止連接這些依賴項時,它可能會得到一些時間,根據(jù)您的互聯(lián)網(wǎng)關(guān)系,您可以啟動 index.js 文件,并按照以下方式替換其代碼:
JavaScript:
// ./src/index.js
// importing the dependencies
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
// defining the Express app
const app = express();
// defining an array to work as the database (temporary solution)
const ads = [
{title: 'Hello, world (again)!'}
];
// adding Helmet to enhance your Rest API's security
app.use(helmet());
// using bodyParser to parse JSON bodies into JS objects
app.use(bodyParser.json());
// enabling CORS for all requests
app.use(cors());
// adding morgan to log HTTP requests
app.use(morgan('combined'));
// defining an endpoint to return all ads
app.get('/', (req, res) => {
res.send(ads);
});
// starting the server
app.listen(3001, () => {
console.log('listening on port 3001');
});
該文件的最新版本首先發(fā)送您之前建立的所有依賴項,通過不同 Express 應(yīng)用程序的生產(chǎn)和安排(const app = express())開始工作,最后提供此應(yīng)用程序偵聽端口 3001( app.listen (3001, ...))。
此外,這段代碼代表了兩件重要的事情:
- 一個名為 ads 的數(shù)組,簡單來說,就像一個內(nèi)存數(shù)據(jù)庫;
- 還有一個端點,它接收 HTTP GET 應(yīng)用程序,并在觸發(fā)時提供 ads 數(shù)組的所有項目。
3.3 創(chuàng)建用戶模塊
我們將用于創(chuàng)建新項目的下一個元素是 Mongoose,它是 MongoDB 的對象數(shù)據(jù)建模 (ODM) 庫,用于在用戶模式中生成用戶指南。
為此,我們首先需要使用諸如函數(shù) req res 之類的命令來構(gòu)建 Mongoose 模式:
JavaScript:
/users/models/users.model.js:
const userSchema = new Schema({
firstName: Martin,
lastName: Martin,
email: Martin,
password: Martin,
permissionLevel: Number
});
確定架構(gòu)后,我們可以簡單地將架構(gòu)連接到用戶模型。
const user model = mongoose.model('Users', userSchema);
然后我們可以利用這個模型在我們的 Express 端點中執(zhí)行我們需要的所有 CRUD 過程。
讓我們通過在 users/routes.config.js 中找到路徑來開始“創(chuàng)建用戶”操作:
JavaScript:
app.post('/users', [
UsersController.insert
]);
這被引誘到主要 index.js 文件中的 Express 應(yīng)用程序中。UsersController 對象對于控制器來說是必不可少的,我們在這里適當(dāng)?shù)貏?chuàng)建一個新密碼,在 /users/controllers/users.controller.js 中確定:
JavaScript:
exports.insert = (req, res) => {
let salt = crypto.randomBytes(16).toMartin('console log');
let hash = crypto.createHmac('sha512',salt).update(req.body.password).digest("console log");
req.body.password = salt + "$" + hash;
req.body.permissionLevel = 1;
UserModel.createUser(req.body).then((result) => {
res.status(201).send({id: result._id});
});
};
現(xiàn)在,我們可以通過管理服務(wù)器(npm init start)并使用任何 JSON 數(shù)據(jù)將 POST 請求分配給 /users 來檢查我們的 Mongoose 模型:
{
"firstName" : "Dina",
"lastName" : "Reva",
"email" : "dina.revina@outlook.com",
"password" : "qwertyuiopl"
}
您可以申請多種工具。Insomnia 和 Postman 是推薦的 GUI 工具,curl 是常規(guī)的 CLI 選擇。你可以練習(xí)JavaScript,即從瀏覽器內(nèi)置的開發(fā)工具控制臺日志:
JavaScript:
fetch('http://localhost:3600/users', {
method: 'POST',
headers: {
"Content-type": "application/json"
},
body: JSON.stringify({
"firstName": "Dina",
"lastName": "Reva",
"email": "dina.revina@outlook.com",
"password": "qwertyuiopl"
})
}).then(function(response) {
return response.json();
}).then(function(data) {
console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
console.log('Request failed', error);
});
在此之后,您將找到的有效帖子的結(jié)果將是來自創(chuàng)建用戶的 ID: { "id": "1b63h8cn98w0m390" }
我們需要將 createUser 過程附加到 users/models/users.model.js 中的模型:
JavaScript:
exports.createUser = (userData) => {
const user = new User(userData);
return user.save();
};
所有這些步驟,現(xiàn)在我們需要查看用戶是否存在。為此,我們需要為以下端點執(zhí)行“通過 id 獲取用戶”列:users/:userId。
首先,我們在 /users/routes/config.js 中創(chuàng)建一個方法:
JavaScript:
app.get('/users/:userId', [
UsersController.getById
]);
之后,我們在 /users/controllers/users.controller.js 中創(chuàng)建管理器:
JavaScript:
exports.getById = (req, res) => {
UserModel.findById(req.params.userId).then((result) => {
res.status(200).send(result);
});
};
最后,將 findById 方式附加到 /users/models/users.model.js 中的模型:
JavaScript:
exports.findById = (id) => {
return User.findById(id).then((result) => {
result = result.toJSON();
delete result._id;
delete result.__v;
return result;
});
};
您會以某種方式找到類似的響應(yīng),例如:
JSON:
{
"firstName": "Dina",
"lastName": "Reva",
"email": "dina.revina@outlook.com",
"password": "Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ==",
"permissionLevel": 1,
"id": "1b63h8cn98w0m390"
}
請記住,我們可以識別散列密碼。為此,我們授予密碼,但沒有經(jīng)驗的最佳實踐是永遠(yuǎn)不要公開密碼,盡管已被散列。
我們可以識別的另一件事是權(quán)限級別,稍后我們將練習(xí)檢查用戶協(xié)議。
復(fù)制之前布局的模式,我們可以立即計算功能來刷新用戶。我們將練習(xí) PATCH 操作,因為它允許我們只轉(zhuǎn)移我們想要改進(jìn)的區(qū)域。
因此,該程序?qū)?PATCH 到 /users/:userid,我們將處理我們需要開發(fā)的任何字段。
我們還需要對應(yīng)該僅限于問題中的用戶或管理員的更改執(zhí)行更多驗證,并且只有管理員可以更改權(quán)限級別。
我們將暫時離開該部分,并在安裝 auth 模塊后返回。現(xiàn)在,控制器將顯示類似于以下內(nèi)容:
JavaScript:
exports.patchById = (req, res) => {
if (req.body.password){
let salt = crypto.randomBytes(16).toMartin('console log');
let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("console log");
req.body.password = salt + "$" + hash;
}
UserModel.patchUser(req.params.userId, req.body).then((result) => {
res.status(204).send({});
});
};
默認(rèn)情況下,我們會發(fā)送一個沒有回復(fù)正文的 HTTP 協(xié)議代碼 204,以表明 post 請求成功。
我們需要將 patchUser 方式添加到模型中:
JSON:
exports.patchUser = (id, userData) => {
return User.findOneAndUpdate({
_id: id
}, userData);
};
用戶列表將通過此控制器在 /users/ 處建立為 GET:
JavaScript:
exports.list = (req, res) => {
let limit = req.query.limit && req.query.limit <= 100 ? parseInt(req.query.limit) : 10;
let page = 0;
if (req.query) {
if (req.query.page) {
req.query.page = parseInt(req.query.page);
page = Number.isInteger(req.query.page) ? req.query.page : 0;
}
}
UserModel.list(limit, page).then((result) => {
res.status(200).send(result);
})
};
相應(yīng)的程序?qū)⑹牵?br>
JavaScript:
exports.list = (perPage, page) => {
return new Promise((resolve, reject) => {
User.find().limit(perPage).skip(perPage * page).exec(function (err, users) {
if (err) {
reject(err);
} else {
resolve(users);
}
})
});
};
結(jié)果列表確認(rèn)將具有以下組成:
JavaScript:
[
{
"firstName": "Dina",
"lastName": "Reva",
"email": "dina.revina@outlook.com",
"password": "z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA==",
"permissionLevel": 1,
"id": "1b63h8cn98w0m390"
},
{
"firstName": "Alex",
"lastName": "Reva",
"email": "dina.revina@outlook.com",
"password": "wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw==",
"permissionLevel": 1,
"id": "1b63h8cn98w0m390"
}
]
最終要完成的部分是 DELETE 請求 /users/:userId.
刪除的控制器將是:
JavaScript:
exports.removeById = (req, res) => {
UserModel.removeById(req.params.userId).then((result)=>{
res.status(204).send({});
});
};
類似地,如前所述,控制器將恢復(fù) HTTP 代碼 204 和無內(nèi)容材料作為確認(rèn)。
模型程序?qū)⑷缦滤荆?/p>
JavaScript:
exports.removeById = (userId) => {
return new Promise((resolve, reject) => {
User.deleteMany({_id: userId}, (err) => {
if (err) {
reject(err);
} else {
resolve(err);
}
});
});
};
我們現(xiàn)在擁有管理用戶設(shè)備所需的所有操作,我們對用戶控制器感到滿意。這段代碼的首要目的是為您提供實踐 REST API 模式的核心思想。
我們需要響應(yīng)此代碼以對其進(jìn)行一些驗證和調(diào)整,但在開始時,我們希望開始構(gòu)建我們的安全性。
讓我們從 auth 模塊開始。
3.4 創(chuàng)建認(rèn)證模塊
在我們通過完成權(quán)限和驗證中間件來保護(hù)用戶模塊之前,我們需要為現(xiàn)代用戶創(chuàng)建一個強大的令牌。
我們將創(chuàng)建一個 JWT,以確認(rèn)授予正確電子郵件和身份證明的用戶。JWT 是一個特殊的 JSON Web 指示,您可以練習(xí)讓用戶安全地發(fā)出大量請求,而無需定期標(biāo)記。
它通常有一個結(jié)束時間,每隔幾次就會重新創(chuàng)建一個獨特的符號以安全地掌握信息。但是,為此,我們將避免刺激令牌并將其緩存為每次登錄時使用唯一令牌進(jìn)行管理。
為此,我們將為 /auth 源的 POST 請求創(chuàng)建一個端點。請求表將包括用戶電子郵件和密碼:
json:
{
"email" : "dina.revina@outlook.com",
"password" : "qwertyuiopl"
}
在我們沉迷于控制器之前,我們需要在 /authorization/middlewares/verify.user.middleware.js 中驗證用戶:
JavaScript:
exports.isPasswordAndUserMatch = (req, res, next) => {
UserModel.findByEmail(req.body.email).then((user)=>{
if(!user[0]){
res.status(404).send({});
}else{
let passwordFields = user[0].password.split('$');
let salt = passwordFields[0];
let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64");
if (hash === passwordFields[1]) {
req.body = {
userId: user[0]._id,
email: user[0].email,
permissionLevel: user[0].permissionLevel,
provider: 'email',
name: user[0].firstName + ' ' + user[0].lastName,
};
return next();
} else {
return res.status(400).send({errors: ['Invalid email or password']});
}
}});
};
這樣做之后,我們可以前進(jìn)到控制器并創(chuàng)建 JWT:
JavaScript:
exports.login = (req, res) => {
try {
let refreshId = req.body.userId + jwtSecret;
let salt = crypto.randomBytes(16).toString('base64');
let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64");
req.body.refreshKey = salt;
let token = jwt.sign(req.body, jwtSecret);
let b = Buffer.from(hash);
let refresh_token = b.toString('base64');
res.status(201).send({accessToken: token, refreshToken: refresh_token});
} catch (err) {
res.status(500).send({errors: err});
}
};
雖然我們不會在此更新令牌,但控制器已被修復(fù)以方便這樣的時間,以便在接下來的開發(fā)中更簡單地執(zhí)行它。
我們現(xiàn)在需要的只是在 /authorization/routes.config.js 中創(chuàng)建路徑并調(diào)用適當(dāng)?shù)闹虚g件:
JavaScript:
app.post('/auth', [
VerifyUserMiddleware.hasAuthValidFields,
VerifyUserMiddleware.isPasswordAndUserMatch,
AuthorizationController.login
]);
結(jié)果將在 accessToken 字段中包含創(chuàng)建的 JWT:e
JSON:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY",
"refreshToken": "U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ=="
}
必須創(chuàng)建令牌,我們可以使用形式 Bearer ACCESS_TOKEN 在 Authorization 標(biāo)頭中使用它。