在前文,我们利用node的原生语法编写了一些简单的node应用,具备基本的请求处理与返回,但这样的处理有一些繁琐,因此,我们可以利用第三方的框架来简化这一系列操作,本文通过整合express框架,来简化node应用的开发流程。
express.js
Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具,因其语法简洁,开发效率高而流行。
express架构模式
Express 的核心架构是基于 中间件(Middleware)链式调用 模式构建的,也称为 “洋葱模型” 或 “职责链模式”,这种设计可以方便的进行各种单一职责的模块的开发,并且十分利于工具的复用,它的核心为以下两个方法:
- app.use:应用中间件
- next():链接到下一个中间件
express安装
通过npm等包管理器来进行安装:
npm install express
express还提供了一系列的中间件,同express一起安装:
- body-parser - node.js 中间件,处理 JSON, Raw, Text 和 URL 编码的数据。
- cookie-parser - 解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。
- multer - node.js 中间件,处理 enctype="multipart/form-data"(设置表单的MIME编码)的表单数据。
express基本语法
在一个express应用中,程序是以app的实例化来开始的:
const express=require('express')
const app=express()
同原生的node语法类似,但express提供了更加简洁的语法(如路由、请求/响应等),下面我们来学习它
路由
相对于传统的node路由编写,express提供了更加简洁的方式,通过对应的请求方法来注册路由:
const express=require('express')
const app=express()//实例化一个express对象
app.get('/',function(req,res){//注册一个/路由,当访问服务器时就会处理
res.send("hello express")
})
//路由也可以接收不同的请求类型
app.post('/send',function(req,res){
res.send('这是一个post请求')
})
const server=app.listen(8088,function(){//初始化服务器
console.log("http://localhost:8088")
})
app.use('/a')//注册中间件路由,一般用于执行一些判断逻辑,如权限、角色、cookie的校验
请求与响应
在express中,请求与响应通过request与response对象的一系列方法与属性来实现,与原生node语法近似,但更为简洁:
例如,我们想发送一个响应,在node原生中是这样的:
res.writeHead(200,{
'Content-Type':'text/plain;charset=utf-8'
})//首先设置响应头
res.end('abc')//发送响应
在express,它可以简化为send:
res.send('abc')//发送响应
可以看到,在express中我们没有显式的设置头,因为在express中,它会根据send的传参类型自动判断响应类型,保证浏览器能够解析标签,从而省去冗杂的样板代码。
下面,我们来介绍一下常用的方法:
常用方法
- request(统一简写为req):
- req.params:路由参数对象,键值对形式,一般用于GET/POST中获取参数,如路由
/user/:id
中req.params.id
。 - req.query:URL 查询字符串参数对象,解析后的键值对,
/search?kw=express
中req.query.kw
- req.body:请求体数据(需配合 body-parser 或内置中间件)。可以读取 JSON、表单、Raw、Buffer 等。
- req.headers:原始请求头数据
- req.get(headerName):获取指定请求头字段值,如获取头中cookie
- req.cookies:解析后的 Cookie 对象(需配合 cookie-parser 中间件)。
- req.signedCookies:解析并验证签名后的 Cookie(需配合 cookie-parser 且使用了签名)。
- req.path:- 请求 URL 的路径部分,不包含查询字符串。
- req.accepts():检查可接受的请求的文档类型
- req.route:当前匹配的路由
- Response(简写为res)
- res.status(code):设置响应码,如常见的200/400/403/500/502.....
- res.send:发送响应体,可以是 Buffer、字符串、对象(会自动转 JSON 并设置 Content-Type),是最常用的方法
- res.json:发送json响应,一般在前后端分离模式中常用
- res.render(view[, locals][, callback]):渲染模版,node集成模版引擎时才使用
- res.redirect([status,] path):重定向到url
- res.format(obj):根据请求头,格式化响应体,如html则格式化响应体为html,text则text等等.....
- res.set(field[, value]):设置响应头
- res.get(field):获取响应头
- res.type(type):设置响应类型
-
res.attachment([filename])
设置Content-Disposition: attachment
,可指定下载文件名。
res.download(path[, filename][, options][, fn])
:提示客户端下载本地文件,内部调用res.sendFile()
并设置为附件。res.cookie(name, value[, options])
设置 Cookie,支持签名、过期、路径、域名、安全等选项。- res.clearCookie(name[, options]):清除 Cookie,效果相当于设置一个过期日期为过去的 Cookie。
- res.append(field, value):追加信息到响应头
静态文件
当需要进行html的页面渲染时,则需要使用express内置的中间件 express.static 来设置静态文件,可以通过app.use来注册一个中间件函数实现:
express=require('express')
app=express()
app.use('/a',express.static('st'))//假设文件存放在st目录下
const server=app.listen(8077,function(req,res){
console.log('http://localhost:8077')
})
当我们输入http://localhost:8077/a/文件名时,就可以直接访问到这些静态文件。
表单提交
在web程序中,我们应用最多的场景一般是各种表单的提交(搜索、登录、注册....),node因为借助于js实现,因此可以方便的联动前端来实现:
我们需要先准备一个表单页面form.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form提交</title>
</head>
<body>
<header>
<h1>Submit Your Details</h1>
</header>
<main>
<form action="/submit" method="POST">
<label for="name">姓名:</label>
<input type="text" id="name" name="name" required><br><br>
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" required><br><br>
<button type="submit">提交信息</button>
</form>
</main>
<footer>
<p>form测试</p>
</footer>
</body>
</html>
接着编写服务器test_form.js:
const express = require('express');
const bodyParser = require('body-parser');//引入请求体解析中间件
const app = express();
const port = 3000;
app.use(bodyParser.urlencoded({ extended: true }));//配置请求体解析中间件
app.use(express.static('st'));
app.get('/', (req, res) => {
res.sendFile(__dirname + '/st/form.html');
});//服务器入口路由返回上文页面
app.get('/index/:id', function (req, res) {
console.log(req.params.id)
loe=req.query
res.send(`id为:${loe.keyword}`)
});
app.post('/submit', (req, res) => {
const { name, email } = req.body;//从请求中解析出name与email字段
res.send(`Form submitted! Name: ${name}, Email: ${email}`);//格式化拼接后返回
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
填写表单字段提交后,可以看到name与email的返回,这样就是一个简单的post表单提交,如果我们需要通过id等数据获取内容,则需要使用get请求来进行获取:
修改test_form.js如下:
const express = require('express');
const bodyParser = require('body-parser');//引入请求体解析中间件
const app = express();
const port = 3000;
app.use(bodyParser.urlencoded({ extended: true }));//配置请求体解析中间件
app.use(express.static('st'));
app.get('/', (req, res) => {
res.sendFile(__dirname + '/st/form.html');
});//服务器入口路由返回上文页面
app.get('/index/:id', function (req, res) {
const simulatedData = {
id: 123,
keyword: 'exampleKeyword'
};//因为没有数据库,使用模拟数据代替
console.log(`Simulated Data: id=${simulatedData.id}, keyword=${simulatedData.keyword}`);
const id = simulatedData.id;
const keyword = req.query.keyword || simulatedData.keyword;
console.log(`Request ID: ${id}`);
res.send(`id为:${keyword}`);
});
app.post('/submit', (req, res) => {
const { name, email } = req.body;//从请求中解析出name与email字段
res.send(`Form submitted! Name: ${name}, Email: ${email}`);//格式化拼接后返回
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
在浏览器输入 http://localhost:3000/index/123 可以看到模拟数据的内容。
文件上传处理
在web开发流程中,文件上传是一个常见的场景,express通过中间件来简化文件流的处理:下面我们来实现一段上传与上传成功的消息返回:
upload.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>文件上传</title>
</head>
<body>
<h2>上传文件</h2>
<!-- 文件上传表单 -->
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file" required />
<button type="submit">上传</button>
</form>
<h2>上传结果</h2>
<!-- 显示上传结果的消息 -->
<p id="message"></p>
<script>
// 检查 URL 参数并显示上传消息
const params = new URLSearchParams(window.location.search);
if (params.has('message')) {
const message = params.get('message');
document.getElementById('message').textContent = message;
}
</script></body></html>
fileload.js:
// 引入 express 模块
const express = require('express');
const multer = require('multer');
const app = express();
const port = 3000;
// 配置中间件解析表单数据
app.use(express.urlencoded({ extended: true }));
// 提供静态文件服务
app.use(express.static('st'));
// 配置 multer 用于文件上传
const upload = multer({ dest: 'st/uploads/' });
// 处理文件上传的路由
app.post('/upload', upload.single('file'), (req, res) => {
if (req.file) {
// 如果文件上传成功,重定向到 fileload.html 并显示成功消息
res.redirect('/fileload.html?message=文件上传成功');
} else {
// 如果文件上传失败,显示错误消息
res.redirect('/fileload.html?message=文件上传失败');
}
});
// 启动服务器并监听指定端口
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
运行后,我们可以看到文件上传成功后有正常的显示,如果我们想实现上传完后的回显,又应该如何处理呢?在常规Node中,我们可能需要手动处理文件流并进行相关返回才能实现该需求,但在express中,我们可以通过express的中间件快速的集成三方库(如multer)来实现图片的上传与回显。
下面是一个express中间件集成multer快速实现图片回显的例子:
首先需要在项目中安装multer:
npm intstall multer
接着我们修改html以应用这些更改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>文件上传</title>
</head>
<body>
<h2>上传文件</h2>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file" required />
<button type="submit">上传</button>
</form>
<h2>上传结果</h2>
<img id="preview" src="" style="max-width: 300px; margin-top: 20px;" />
<script>
// 检查 URL 参数并回显图片(设置文件路径属性)
const params = new URLSearchParams(window.location.search);
if (params.has('filename')) {
const filename = params.get('filename');
document.getElementById('preview').src = `/uploads/${filename}`;
}
</script>
</body>
</html>
接着编写fileload.js:
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
// 设置静态目录
app.use(express.static('st'));
app.use('/uploads', express.static('uploads'));
// 设置 multer 存储路径与文件名
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads'); // 上传目录
},
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname)); // 使用时间戳命名
},
});
const upload = multer({ storage: storage });
// 处理上传请求
app.post('/upload', upload.single('file'), (req, res) => {
const filename = req.file.filename;
res.redirect(`/testload.html?filename=${filename}`);
});
// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
console.log(`服务器已启动: http://localhost:${PORT}/testload.html`);
});
这样,我们就实现了一个简单的node文件上传回显示例了,当文件上传成功后,node服务器会返回它的路径进行Img的src设置,从而实现一个简单的上传回显。
Cookie
在web开发中,我们通常需要进行各种表单以及数据的获取与提交,但这些操作大部分是重复的,那么,有没有什么方法能够复用这些操作的数据以带给用户更佳的体验呢?在web开发中,可以使用session/cookie/token来进行这些数据的存储。其中,session由于抗并发能力差,因此在当今的前后端分离架构中使用较少,而cookie因为存储在浏览器上,因此大范围用于存储状态等信息。
node提供了丰富的中间件,可以快速的集成cookie。
首先我们需要安装对应的中间件:
npm install cookie-parser
然后,我们需要在express对其进行中间件注册:
const cookieParser = require('cookie-parser');//引入并实例化
app.use(cookieParser())
在注册之后,我们就可以使用cookieparser来进行cookie的相关操作了,cookieparser的常用操作有三个:
- res.cookie():设置cookie
- res.cookies:cookie属性,用于获取cookie
- res.clearCookie:用于对cookie进行删除
下面,我们通过一个html+js来实现基本的cookie操作
cookie.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cookie Demo</title>
</head>
<body>
<h1>Cookie Demo</h1>
<button id="setCookie">设置Cookie</button>
<button id="getCookie">读取Cookie</button>
<button id="clearCookie">清除Cookie</button>
<p id="output"></p>
<script>
document.getElementById('setCookie').addEventListener('click', () => {
fetch('/set-cookie')
.then(response => response.text())
.then(data => {
document.getElementById('output').textContent = data;
});
});
document.getElementById('getCookie').addEventListener('click', () => {
fetch('/get-cookie')
.then(response => response.text())
.then(data => {
document.getElementById('output').textContent = data;
});
});
document.getElementById('clearCookie').addEventListener('click', () => {
fetch('/clear-cookie')
.then(response => response.text())
.then(data => {
document.getElementById('output').textContent = data;
});
});
</script>
</body>
</html>
cookie.js:
const express = require('express');
const cookieParser = require('cookie-parser');
const path = require('path');
const app = express();
const port = 3000;
// 使用cookie-parser中间件
app.use(cookieParser());
// 定向到cookie_demo.html
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'st', 'cookie_demo.html'));
});
// 设置cookie的路由
app.get('/set-cookie', (req, res) => {
res.cookie('username', 'testuser', { maxAge: 900000, httpOnly: true });
res.send('Cookie已设置');
});
// 读取cookie的路由
app.get('/get-cookie', (req, res) => {
const username = req.cookies['username'];
if (username) {
res.send(`Cookie中的用户名是: ${username}`);
} else {
res.send('未找到用户名的Cookie');
}
});
// 删除cookie的路由
app.get('/clear-cookie', (req, res) => {
res.clearCookie('username');
res.send('Cookie已清除');
});
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
运行服务器,点击对应的按钮,我们可以看到这三个操作都被触发。