NodeJS-04-express
基础操作
服务启动
import express from 'express'
const app = express()
app.get('/', (req, res) => {
res.send('Hello, World!')
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
获取请求参数
import express from 'express'
const app = express()
app.get('/login', (req, res) => {
// 获取请求参数
const username = req.query.username
const password = req.query.password
// 校验参数
if (!username || !password) {
return res.send('username or password is empty')
}
res.send(`welcome 【${username}】`)
console.log('用户登录参数: ', username, password)
// 获取请求头
const userAgent = req.headers['user-agent']
console.log('用户登录请求头: ', userAgent)
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
获取路由参数
import express from 'express'
const app = express()
// 获取路由参数
app.get('/shop/:id', (req, res) => {
const shopId = req.params.id
res.send(`shop id is 【${shopId}】`)
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
响应设置
import express from 'express'
const app = express()
// 响应设置
app.get('/resp', (req, res) => {
// express 响应 支持链式编程
res.status(200) // 设置响应状态码
res.set('Content-Type', 'text/plain') // 设置响应头
res.send('response body') // 设置响应体
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
其他响应体
import express from 'express'
const app = express()
app.get('/resp-other', (req, res) => {
res.json({ name: 'express' }) // 发送 JSON 响应
res.redirect('http://www.baidu.com') // 重定向
res.download('./package.json') // 下载文件
res.sendFile(__dirname + '/index.html') // 响应文件内容
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
express中间件
概念
在 Express 框架中,中间件(Middleware) 是其核心概念之一,它指的是一个在请求(Request)到达最终的路由处理函数之前,以及响应(Response)返回给客户端之前,可以被调用的特殊函数。
这些函数可以访问请求对象 (req)、响应对象 (res) 和应用程序中的下一个中间件函数 (next)
本质是一个回调函数
中间件的主要功能包括执行任何代码、修改请求和响应对象、终结请求-响应循环、调用下一个中间件等。根据其作用范围,主要可以分为:
- 全局中间件
- 路由中间件
| Express 概念 | 核心特点 | Java 中的近似对应 | 对应关系说明 |
|---|---|---|---|
| 全局中间件 | 通过 app.use()注册,对所有进入服务器的请求生效 | Servlet 过滤器 (Filter) | 两者均在请求处理链的最外层工作,对所有请求进行预处理(如日志、鉴权)和后处理 |
| 路由中间件 | 通过路由方法(如 app.get(), app.post())或路由级 Router 绑定,对特定路由生效 | Spring 拦截器 (Interceptor) / AOP 切面 | 两者都能更精细地控制拦截范围(特定URL、Controller方法),实现权限检查、日志记录等业务逻辑 |
🛠️ 实践建议
- 注册顺序很重要:中间件的注册顺序决定了它们的执行顺序。例如,通常需要将日志记录、CORS 处理等中间件放在最前面,而错误处理中间件必须放在所有路由之后。
- 避免阻塞:在编写中间件时,要确保逻辑高效,避免长时间同步操作阻塞事件循环,从而影响服务器性能。
- 灵活组合:在实际项目中,可以根据需求灵活组合使用全局中间件和路由中间件。例如,使用全局中间件进行基础安全防护和日志记录,对特定的管理后台路由使用路由中间件进行更严格的身份验证。
代码示例
import express from 'express'
import fs from 'fs'
const app = express()
// 全局中间件:中间件应该放在路由之前定义
app.use(function (req, res, next) {
let { url, ip } = req
fs.appendFile('./log.txt', `${url} ${ip}\n`, (err) => {
if (err) {
console.log('写入日志失败', err)
}
})
next()
})
// 路由中间件:在处理函数之前执行
let checkCodeMiddleware = (req, res, next) => {
// 判断请求参数中是否包含code参数
let { code } = req.query
if (!code) {
return res.send('code is empty')
}
next()
}
// 路由中间件:在处理函数之前执行
app.get('/home', checkCodeMiddleware, (req, res) => {
res.send('前台首页')
})
// 路由中间件:在处理函数之前执行
app.get('/admin', checkCodeMiddleware, (req, res) => {
res.send('后台首页')
})
// 使用app.use处理404页面,确保放在所有路由之后
app.use((req, res) => {
res.status(404).send('<h1>404 Not Found</h1>')
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
静态资源中间件
通过一行代码搞定:
app.use(express.static(__dirname + '/public'))
静态资源示例代码
import express from 'express'
import path from 'path'
import { fileURLToPath } from 'url'
const app = express()
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// 静态资源中间件:将public目录下的文件作为静态资源提供
app.use(express.static(__dirname + '/public'))
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
注意事项
- index.html 文件为默认打开的资源
- 如果静态资源与路由规则同时匹配,谁先匹配谁就响应
- 路由响应动态资源,静态资源中间件响应静态资源
express获取请求体的中间件
核心使用内置中间件:
app.use(express.urlencoded({ extended: false }))
app.use(express.json())
import express from 'express'
const app = express()
// 在Express 5.x中,建议全局使用这些中间件
// 对于表单数据 (application/x-www-form-urlencoded)
app.use(express.urlencoded({ extended: false }))
// 对于JSON数据 (application/json)
app.use(express.json())
app.post('/register', (req, res) => {
const body = req.body
console.log('注册参数: ', body)
res.send('register success')
})
app.post('/save', (req, res) => {
const body = req.body
console.log('保存参数: ', body)
res.send('save success')
})
// 添加一个简单的表单页面用于测试
app.get('/form', (req, res) => {
res.send(`
<form action="/save" method="post">
<label>姓名: <input type="text" name="name"></label><br>
<label>邮箱: <input type="email" name="email"></label><br>
<button type="submit">提交</button>
</form>
`)
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
路由
概念
express 中的 Router 是一个完整的中间件和路由系统,可以看做是一个小型的 app 对象。
作用
对路由进行模块化,更好的管理路由
示例
先封装路由
// ./router/home.js
import express from 'express'
const router = express.Router()
// 定义首页路由
router.get('/', (req, res) => {
res.send('<h1>首页</h1>')
})
// 定义登录路由
router.get('/login', (req, res) => {
res.send('<h1>登录页</h1>')
})
// 导出路由模块
export default router
服务中引用
import express from 'express'
import homeRouter from './router/home.js'
const app = express()
app.use('/', homeRouter)
app.use('/user', homeRouter)// 内部的路由会自动添加前缀,类似java的@RequestMapping("/users")
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
EJS模板引擎
初体验
import ejs from 'ejs'
let person = ['张三', '李四', '王五']
let html = ejs.render('<%= person.join(",") %>', { person })
console.log(html)
express使用模板引擎
- 构建模板文件夹及模板引擎,注意模板引擎拓展名是
ejs,如:./views/user.ejs - 服务端
- 设置模板引擎为 EJS
- 指定模板目录
- 渲染数据
- 引擎基础语法
- 用
<%= %>语法输出变量值 - 使用
<% %>包裹 JavaScript 代码逻辑
- 用
代码示例:
ejs文件:./views/user.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2><%= title %></h2>
<h3>学生列表</h3>
<!-- 条件判断 -->
<% if (hasRegister) { %>
<p>用户已注册</p>
<% } else { %>
<p>用户未注册</p>
<% } %>
<table border="1" cellpadding="5">
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
<!-- 控制结构 -->
<% person.forEach(item => { %>
<tr>
<!-- 输出变量 -->
<td><%= item.name %></td>
<td><%= item.age %></td>
</tr>
<% }) %>
</table>
</body>
</html>
服务端
import express from 'express'
import path from 'path'
const app = express()
// 设置模板引擎为 EJS
app.set('view engine', 'ejs')
// 设置模板文件存放位置 注意:ES6模块中一定要用全路径
app.set('views', path.join(import.meta.dirname, './views'))
let person = [
{ name: '张三', age: 18 },
{ name: '李四', age: 20 },
{ name: '王五', age: 22 }
]
let hasRegister = true
app.get('/user', (req, res) => {
res.render('user', { person, title: '用户列表(我是EJS渲染数据)', hasRegister })
})
app.listen(9999, () => {
console.log('Server is running on port 9999')
})
generator工具
初始化
安装
npm i -g express-generator
使用
express -e # 新建脚手架
yarn # 安装依赖
yarn start # 跑项目
# 访问端口 3000
使用 npx 创建
npx express-generaor -e
文件上传
以头像上传为例:
前端部分
- 添加路由
router.get('/portrait', (req, res) => {
res.render('portrait')
})
- 模板构建
views/portrait.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>用户头像</h2>
<form action="/portrait" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username" required><br/>
头像:<input type="file" name="avator" required><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
后端部分
- 安装依赖
npm i formidable / yarn add formidable - 添加路由
var { formidable } = require('formidable'); // 注意:v3.x版本中它不再直接导出一个函数,而是导出一个对象
router.post('/portrait', (req, res, next) => {
// 创建表单对象
const form = formidable({
multiples: true,
uploadDir: __dirname + '/../public/images', // 指定上传地址
keepExtensions: true, // 是否保持扩展名
})
// 解析请求报文
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
console.log(fields, files);
let fileUrl = '/images/' + files.avator[0].newFilename;
// 响应
res.send(fileUrl);
});
});
ts构建工具
npx express-generator-typescript 项目名
cd 项目文件夹
git init .