Node.js 中的 HTTP 模块
Node.js 附带内置的 HTTP 模块。 这是一个相当小的模块,用于处理大多数类型的请求。 它支持常见类型的数据,例如标头、URL 和有效负载。
以下类可在整个过程中帮助管理请求:
http.Server:表示 HTTP Server 的实例。 需要指示此对象侦听特定端口和地址上的不同事件。http.IncomingMessage:此对象是由http.Server或http.ClientRequest创建的可读流。 使用它访问状态、标头和数据。http.ServerResponse:此对象是 HTTP 服务器在内部创建的流。 此类定义响应应有的外观,例如标头的类型和响应内容。
HTTP | Node.js v18.9.1 Documentation (nodejs.org)
const http = require("http")
const PORT = 9000
const app = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
data: 'Hello World!'
}))
})
app.listen(PORT,()=>{
console.log(`listening on port ${PORT}`);
})
使用 Express 框架创建新的 Web 应用 - Training | Microsoft Learn
express框架
构建一些 Web 应用程序后,你会注意到你在反复解决相同的问题。 路由管理、身份验证和授权、错误管理等问题很常见。 此时,你会开始寻找可解决其中部分或全部问题的库或框架。
为什么要将 Express 作为构建下一个应用的框架?
- 良好功能:Express 提供了一系列功能,使你能够快速、高效地工作。
- 使复杂性抽象化:例如,Express 使复杂概念(例如流)抽象化,并使整个开发体验变得更加容易。
- 解决常见的 Web 问题:Express 有助于解决路由管理、缓存和重定向等常见问题。
- 由数百万开发者信任:根据 GitHub,目前有 680 万开发者对其 Web 应用程序使用 Express。
npm install express
提供静态内容的路由
const express = require("express");
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello World!'));
app.get("/products", (req,res) => {
const products = [
{
id: 1,
name: "hammer",
},
{
id: 2,
name: "screwdriver",
},
{
id: 3,
name: "wrench",
},
];
res.json(products);
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
使用中间件管理请求生命周期
考虑将请求作为一系列步骤来处理。 如果用户需要登录才能处理请求的资源,则步骤可能如下所示:
- Pre 请求:调查用户是否通过请求标头发送了正确的凭据。 如果验证了凭据,则将请求发送到下一步。
- 构造响应:与某种数据源(如数据库或终结点)通信。 只要请求正确请求资源,此步骤就会返回资源。
- Post 请求: 一个可选步骤,用于在请求处理后运行一段代码。 出于日志记录目的,可以运行此步骤。
Express 框架对以此方式处理请求提供内置支持。 若要运行 pre 或 post 请求,需要对 Express 实例化对象实现 use() 方法。 Express 中的 pre 或 post 请求称为“中间件(middle-ware)”,具有以下语法形式:
app.use((req, res, next) => {})
递给 use() 方法的方法具有三个参数:req、res 和 next。 这些参数具有以下含义:
req:包含请求标头和调用 URL 的传入请求。 如果客户端随请求发送数据,它也可能具有一个数据主体。res:用于写入要发送回调用客户端的标头和数据等信息的响应流。next:指示请求正常并已准备好处理的参数。 如果未调用next(),则请求的处理将停止。 此外,最佳做法是告诉客户端请求未处理的原因,例如调用res.send('\<specify a reason why the request is stopped>'\)。
请求管道
需要在请求之前运行的中间件(pre 请求)定义在实际请求前。
需要在请求之后运行的中间件(post 请求)定义在实际请求后。
app.use((req, res, next) => { // Pre request }) app.get('/protected-resource', () => { // Handle the actual request }) app.use((req, res, next) => { // Post request }) app.get('/login', () => {})还可以将 pre 请求中间件代码作为处理请求的参数来运行
app.get( '/<some route>', () => { // Pre request middleware }, () => { // Handle the actual request })import { isAuthorized } from "./helper.js"; import express from "express"; const app = express(); const port = 3000; app.get("/", (req, res) => res.send("Hello World!")); app.get("/users", isAuthorized,(req, res) => { res.json([ { id: 1, name: "User Userson", }, ]); }); app.get("/products", (req, res) => { res.json([ { id: 1, name: "The Bluest Eye", }, ]); }); app.listen(port, () => console.log(`Example app listening on port ${port}!`));export function isAuthorized(req, res, next) { const auth = req.headers.authorization; if (auth === 'secretpassword') { next(); } else { res.status(401); res.send('Not permitted'); } }
配置应用以接收数据
- “导入正文分析器”。 需要将传入数据转换为可读的格式。 导入随 Express 一起安装的库
body-parser:
let bodyParser = require('body-parser');
配置数据。 配置 Express 以将传入的正文数据分析为预期格式。 以下代码将数据转换为 JSON:
app.use(bodyParser.json({ extended: false }));客户端发送的数据现在可用于
req请求对象上的body属性。 现在,你可以读取此数据并与数据源对话。 然后,还可以从该数据创建资源或更新资源,具体取决于请求使用 POST 还是 PUT 谓词。
app.post('/<path>', (req, res) => {
console.log('req.body', req.body) // contains incoming data
})
CRUD
为资源实现 CRUD 是一项常见任务。 Express 有一种 route() 方法正用于此目的。 使用 route()方法时,可以对代码进行分组,使其更易于阅读。
const express = require('express')
const app = express()
const port = 3000
let bodyParser = require('body-parser');
app.use(bodyParser.json());
let products = [];
app.route('/products')
.get((req, res) => {
res.json(products);
})
.post((req, res) => {
const newProduct = { ...req.body, id: products.length + 1 }
products = [...products, newProduct]
res.json(newProduct);
})
.put((req, res) => {
let updatedProduct;
products = products.map(p => {
if (p.id === req.body.id) {
updatedProduct = { ...p, ...req.body };
return updatedProduct;
}
return p;
})
res.json(updatedProduct);
})
.delete((req, res) => {
const deletedProduct = products.find(p => p.id === +req.body.id);
products = products.filter(p => p.id !== +req.body.id);
res.json(deletedProduct);
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
路由参数
delete可以带body
app.delete('/products/:id', function(req, res) {
const deletedProduct = products.find(p => p.id === +req.params.id);
products = products.filter(p => p.id !== +req.params.id);
res.json(deletedProduct);
});
发送参数
const http = require('http');
const productToDelete = {
id: 1
}
const data = JSON.stringify(productToDelete)
const options = {
hostname: 'localhost',
port: 3000,
path: `/products/${productToDelete.id}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
const request = http.request(options, (res) => {
let body = '';
res.on('data', (chunk) => { body += "" + chunk; })
res.on('error', (err) => console.error('err', err))
res.on('end', () => { console.log('response', body) })
res.on('close', () => { console.log('Closed connection') })
})
request.end(data);
