1. 介绍
-
express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架,简单来说,express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用( HTTP 服务)
-
官方网址: https://www.expressjs. com.cn/
2. express基本使用
2.1 安装依赖包
npm init -y
npm i express
2.2 初体验
// 1.引入express
const express = require('express')
// 2.创建服务对象
const app = express()
// 3.创建路由
app.get('/home',(req,res)=>{
res.send('首页~')
})
// 4.监听端口,启动服务
app.listen(3000,()=>{
console.log('server running~');
})
3. express路由
3.1 什么是路由?
- 官方定义: 路由确定了应用程序如何响应客户端对特定端点的请求
3.2 路由的使用
-
一个路由的组成有 请求方法 , 路径 和 回调函数 组成,express 中提供了一系列方法,可以很方便的使用路由,使用格式如下: app . < method > ( path , callback )
// 1.引入express const express = require('express') // 2.创建服务对象 const app = express() // 3.创建路由 app.get('/home',(req,res)=>{ res.send('首页~') }) app.get('/',(req,res)=>{ res.send('我才是真正的首页~') }) app.post('/login',(req,res)=>{ res.send('登录成功~') }) app.all('*',(req,res)=>{ res.send('<h1>Not Found<h1/>') }) // 4.监听端口,启动服务 app.listen(3000,()=>{ console.log('server running~'); })
4. 获取请求报文参数
4.1 获取请求行以及请求头参数
- 1-3是原生http获取请求报文的属性,后面是express获取请求报文的属性以及方法
- 获取请求方法:req.method
- 获取请求url:req.url
- 获取请求协议版本号:req.httpVersion
- 获取ip:req.ip
- 获取请求路径:req.url
- 获取请求头:req.headers
- 获取查询字符串对象:req.query
- 获取请求头里边的内容:req.get('请求头的key') 通过get方法获取
- 完整代码如下:
// 1.引入express const express = require('express') // 2.创建服务对象 const app = express() // 3.创建路由 app.get('/request',(req,res)=>{ // 原生http的请求也支持 // 获取请求方法 console.log(req.method) // GET // 获取请求url路径 console.log(req.url) // /request // 获取协议版本号 console.log(req.httpVersion) // 1.1 // 获取请求头 console.log(req.headers) // { // host: '127.0.0.1:3000', // connection: 'keep-alive', // 'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"', // 'sec-ch-ua-mobile': '?0', // 'sec-ch-ua-platform': '"Windows"', // 'upgrade-insecure-requests': '1', // 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', // accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', // 'sec-fetch-site': 'none', // 'sec-fetch-mode': 'navigate', // 'sec-fetch-user': '?1', // 'sec-fetch-dest': 'document', // 'accept-encoding': 'gzip, deflate, br', // 'accept-language': 'zh-CN,zh;q=0.9' // } // express内部封装的获取请求报文参数的方法 // 获取ip console.log(req.ip) // 127.0.0.1 // 获取路径 console.log(req.path) // /request // 获取查询字符串 console.log(req.query) // 访问http://127.0.0.1:3000/request?a=1&b=2 { a: '1', b: '2' } // 获取请求头 get方法 console.log(req.get('host')) //127.0.0.1:3000 res.send('Hello Express') }) // 4.监听端口,启动服务 app.listen(3000,()=>{ console.log('server running...') })
4.2 获取路由参数
获取路由参数对象:req.params
- 完整代码如下:
// 1.引入express const express = require('express') // 2.创建服务对象 const app = express() // 3.创建路由 app.get('/goods/details/:id',(req,res)=>{ // req.params 来获取路由参数对象 .id和占位符:id对象 参数可随意命名 const id = req.params.id res.send(`当前页是id为${id}的详情页`) }) // 4.监听端口,启动服务 app.listen(3000,()=>{ console.log('server running...'); })
4.3 路由参数练习
- 完整代码如下:
// 1.引入express const express = require('express') // 2.创建服务对象 const app = express() /** * 导入json数据 可以使用fs读取 但没必要 * require引入的json是一个对象可以直接使用 */ const { singers } = require('./5-singer') // 3.创建路由 app.get('/singers/detail/:id', (req, res) => { const result = singers.find(item => { return item.id === Number(req.params.id) }) if (result) { res.send(` <!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> <h1>${result.name}</h1> <img src='${result.pic}'> </body> </html> `) } else { res.send('<h1>Not Found</h1>') } res.send('嗨嗨') }) // 4.监听端口,启动服务 app.listen(3000, () => { console.log('server running...'); })
4.4 获取请求体
4.4.1 安装第三方包 body-parser , 它是一个中间件
npm i body-parser
4.4.2 引入
// 引入body-paser中间件
const bodyParser = require('body-parser')
// 获取json格式的请求体
const jsonParser = bodyParser.json()
// 获取querystring格式的请求体
const urlencodedParser = bodyParser.urlencoded({ extended: false })
4.4.3 引用
app.post('/login',urlencodedParser,(req,res)=>{
// 使用第三方包body-parser这个中间件来获取请求体
// 这个中间件会想请求体req身上追加一个body属性,这个body存的就是请求体
res.send(req.body)
})
4.4.4 完整代码
/*
实现:
get /login 响应表单页面
post /login 获取请求体
*/
// 引入express
const express = require('express')
const path = require('path')
// 1.安装body-parser中间件 npm i body-parser
// 引入body-paser中间件
const bodyParser = require('body-parser')
// 获取json格式的请求体
const jsonParser = bodyParser.json()
// 获取querystring格式的请求体
const urlencodedParser = bodyParser.urlencoded({ extended: false })
// 创建服务对象
const app = express()
// 创建路由规则
app.get('/login',(req,res)=>{
res.sendFile(path.resolve(__dirname+'/10-login.html'))
})
app.post('/login',urlencodedParser,(req,res)=>{
// 使用第三方包body-parser这个中间件来获取请求体
// 这个中间件会想请求体req身上追加一个body属性,这个body存的就是请求体
res.send(req.body)
})
// 监听端口,启动服务
app.listen(3000,()=>{
console.log('服务已启动...')
})
5. 响应设置
- 1-4是原生http的响应设置,后面是express的响应设置
- 置响应状态码:res.statusCode = 200
- 设置响应状态码描述:res.statusMessage = 'OK'
- 设置响应头:res.setHeaser(key,value)
- 设置响应体:res.write(字符串/buffer) 可以有多个,res.end(字符串/buffer) 有且只有一个
- 设置响应状态码:res.status(200)
- 设置响应头:res.set(key,value)
- 设置响应体:res.send(字符串/buffer)
- 完整代码如下:
// 1.引入express const express = require('express') // 2.创建服务对象 const app = express() // 3.创建路由 app.get('/home', (req, res) => { // 原始http响应设置 // 设置响应状态码 // res.statusCode = 201 // 设置响应状态码描述 // res.statusMessage = 'No Data' // 设置响应头 // res.setHeader('aaa', 'bbb') // res.setHeader('content-type', 'text/html;charset=utf-8') // 设置响应体 // res.write('嗨嗨嗨,') // res.write('来了啊') // res.end('老八') // express封装的响应设置 // 设置响应状态 // res.status(201) // 设置响应头 // res.set('ccc', 'ddd') // 设置响应体 // res.send('我是express设置响应体的方法,我不用设置content-type响应头都不会乱码') // 重定向响应 // res.redirect('http://www.baidu.com') // 下载响应 接收一个绝对路径作为参数 // res.download(__dirname + '/package.json') // json响应 // res.json({ // "name": "张三", // "age": 18 // }) // 响应一个文件 接受一个绝对路径作为参数 // res.sendFile(__dirname + '/2-路由.js') }) // 4.监听端口,启动服务 app.listen(3000, () => { console.log('服务已启动...'); })
6. 中间件
6.1 介绍
- 什么是中间件?
- 中间件本质上就是一个回调函数,中间件函数 可以像路由回调一样访问 请求对象(request) , 响应对象(response)
6.2 中间件的类型
- 全局中间件
路由中间件
6.2.1 全局中间件
- 每一个请求 到达服务端之后 都会执行全局中间件函数
-
声明全局中间件函数:
// 定义一个全局中间件 用于记录访客 function recordMiddleWare(req, res, next) { // 实现功能代码 const { url, ip } = req const data = ` ${ip}在${dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')}访问了 ${url}\r\n ` fs.appendFileSync(__dirname + '/access.log', data) next() // 执行next函数(如果希望执行完中间件函数之后,仍然继续执行路由中的回调函数,必须调用next) }
-
应用中间件
app.use(recordMiddleWare)
-
声明时可以直接将匿名函数传递给 use,可以定义多个全局中间件
app.use((req, res, next)=>{ console.log('定义第一个测试中间件'); next(); }) app.use((req, res, next)=>{ console.log('定义第二个测试中间件'); next(); })
-
记录访客代码例子:
// 记录每个请求的url 和 IP地址 以及访问时间 // 1.引入express const express = require('express') const fs = require('fs') const dayjs = require('dayjs') // 2.创建服务对象 const app = express() // 定义一个全局中间件 用于记录访客 function recordMiddleWare(req, res, next) { // 实现功能代码 const { url, ip } = req const data = ` ${ip}在${dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')}访问了 ${url}\r\n ` fs.appendFileSync(__dirname + '/access.log', data) next() // 执行next函数(如果希望执行完中间件函数之后,仍然继续执行路由中的回调函数,必须调用next) } // 应用中间件 app.use(recordMiddleWare) // 3.创建路由 app.get('/home', (req, res) => { res.send('首页') }) app.get('/admin', (req, res) => { res.send('管理页') }) // 4.监听端口,启动服务 app.listen(3000, () => { console.log('server running...'); })
-
6.2.2 路由中间件
- 每一个使用路由中间件的请求都会先执行路有2中间件:
- 生命路由中间件函数:
// 创建路由中间件 function routerWare(req,res,next){ req.query.code === '521' ? next() : res.send('暗号错误') }
- 应用路由中间件:
- 完整代码:
/* 针对 /admin /setting的请求 要求URL携带code=521参数 如果未携带提示暗号错误 */ // 1.引入express const express = require('express') // 2.创建服务对象 const app = express() // 创建路由中间件 function routerWare(req,res,next){ req.query.code === '521' ? next() : res.send('暗号错误') } // 3.创建路由规则 app.get('/admin',routerWare,(req,res)=>{ res.send('管理页面') }) app.get('/setting',routerWare,(req,res)=>{ res.send('配置页面') }) // 4.监听端口,启动服务 app.listen(3000,()=>{ console.log('server running...'); })
-
6.2.3静态资源中间件express.staic()内置中间件
- 完整代码:
// 1.引入epxress const express = require('express') const path = require('path') // 2.创建服务对象 const app = express() /** * 静态资源中间件 * - express.static() * - 参数: 静态资源目录的绝对路径 * * 静态资源目录即是网站的根目录,客户端访问服务器, * 服务器去哪个文件夹去找资源,这个文件夹就是静态资源目录 * * 注意:访问网站127.0.0.1:3000 路径默认为'/' * 服务端会去静态资源根目录下去找index.html将其返回 * 如果路由规则中也有'/' 谁先匹配到谁生效,也就是谁在上面谁生效 * */ const staticMiddleWare = express.static(path.resolve(__dirname+'/public')) app.use(staticMiddleWare) // 3.创建路由规则 app.get('/home',(req,res)=>{ res.send('首页') }) // 4.监听端口,启动服务 app.listen(3000,()=>{ console.log('server running'); })
- 注意事项:
- index.html文件为默认打开的资源 http://127.0.0.1:3000/index.html等同于http://127.0.0.1:3000
- 如果静态资源与路由规则同时匹配,则谁先匹配到就响应谁
- 一般来说,路由响应动态资源,静态资源中间件响应静态资源
7.路由模块化
- 新建routers文件夹 , 文件夹里面新建路由模块的js文件
- 在文件中编写以下代码:
// adminRouter.js // 1.引入express const express = require('express') // 2.创建路由实例 const router = express.Router() // 3.创建路由规则 router.get('/admin',(req,res)=>{ res.send('管理页面') }) router.get('/log',(req,res)=>{ res.send('日志页面') }) //4.导出路由实例 module.exports = router
// homeRouter.js // 1.引入express const express = require('express') // 2.创建路由实例 const router = express.Router() // 注意方法Router是大写 // 3.创建路由规则 router.get('/home',(req,res)=>{ res.send('前台首页') }) router.get('/login',(req,res)=>{ res.send('登录页面') }) // 4.导出路由实例 module.exports = router
在server.js文件中 引入路由模块
// server.js // 引入express const express = require('express') // 引入路由模块 const homeRouter = require('./routers/homeRouter') const adminRouter = require('./routers/adminRouter') // 创建应用实例 const app = express() app.use(homeRouter) app.use(adminRouter) // 监听端口,启动服务 app.listen(3000,()=>{ console.log('server running...'); })
8.模板引擎ejs
- 1. 什么是模板引擎?
模板引擎是用于用户界面 和业务数据分离的一种技术,但随着前后端分离,这种技术就少了 - 2. 安装ejs
npm i ejs
- 3. ejs初体验 <%= %>语法
ejs.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ejs</title>
</head>
<body>
<!-- <%= %> ejs的模板语法 类似于模板字符串${} -->
<h1>
<%= weather %>
</h1>
</body>
</html>
// 1.引入ejs
const ejs = require('ejs')
// 2.定义数据
let weather = '今天天气真不错!'
// 3.引入页面
const fs = require('fs')
const html = fs.readFileSync('./12-ejs.html').toString()
// 4.渲染界面
const result = ejs.render(html,{weather:weather})
console.log(result);
- 4. ejs列表渲染 <%= %> 包裹的是变量相当于${} , <% %>包裹的js语法,相当于 ``
// ejs列表渲染.js
<!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>
<ul>
<% xiyou.forEach(item=>{ %>
<li><%= item %></li>
<% }) %>
</ul>
</body>
</html>
// ejs.js
const fs = require('fs')
const html = fs.readFileSync('./13.ejs列表渲染.html').toString()
let res = ejs.render(html,{xiyou:xiyou})
console.log(res);
- 5.ejs条件渲染
// ejs.html
<!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>
<header>
<% if(isLogin){ %>
<span>欢迎回来</span>
<% }else{ %>
<button>登录</button> <button>注册</button>
<% } %>
</header>
</body>
</html>
// ejs.js
const ejs = require('ejs')
const fs = require('fs')
let html = fs.readFileSync('./14.ejs条件渲染.html').toString()
let result = ejs.render(html,{isLogin:isLogin})
console.log(result);
6. ejs在express中的应用
创建views文件夹 , 在views文件夹下创建ejs在express中的应用.ejs文件 切记是ejs后缀名的文件
// 在.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>
<h1>
<%= title %>
</h1>
</body>
</html>
// 导入express
const express = require('express')
// 引入path
const path = require('path')
// 创建应用实例
const app = express()
// 1.设置模板引擎
app.set('view engine','ejs')
// 2.设置模板文件存放位置,模板文件:具有模板语法内容的文件
app.set('views',path.resolve(__dirname,'./views'))
// 创建路由规则
app.get('/home',(req,res)=>{
// 3.render响应
// res.render('模板文件名','数据')
let title = '测试ejs在express中的应用'
// 第一个参数要和模板文件的名字保持一致
res.render('ejs在express中的应用',{title:title})
})
// 监听端口,启动服务
app.listen(3000,()=>{
console.log('server running...');
})
9. express生成器generator (脚手架)
- 全局安装
npm i express-generator -g
安装之后,全局多了一个命令 express, 通过express -h 查看可用命令
使用express -e 项目名 来初始化项目
初始化好项目之后,安装依赖: npm i
看package.json文件中的脚本 启动服务
端口默认是3000
10.文件上传
1.views文件夹下编写前端模板,上传文件form表单页面
// views文件夹下的 upoload.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> <h1>文件上传</h1> <form action="/handleUpload" method="post" enctype="multipart/form-data"> <input type="text" name="文件类型"> <br><br> <input type="file" name="上传文件"> <br><br> <button>提交</button> </form> </body> </html>
2.文件上传.js
// 文件上传.js // 1.引入express const express = require('express') const path = require('path') const formidable = require('formidable') // 2.创建实例 const app = express() app.set('view engine','ejs') //设置模板引擎 app.set('views',path.resolve(__dirname,'./views')) // 设置模板引擎查找目录 app.use(express.static(__dirname+'/public')) // 3.创建路由规则 app.get('/home',(req,res)=>{ res.render('upload') // 响应模板引擎文件 upload.ejs }) app.post('/handleUpload',(req,res)=>{ // res.send('ok ') const form = formidable({ multiples:true, keepExtensions:true, uploadDir:__dirname+'/public/images' }); form.parse(req, (err, fields, files) => { if (err) { next(err); return; } // res.json({ fields, files }); res.send('/images/'+files['上传文件'].newFilename) }); }) // 4.监听端口,启动服务 app.listen(3000,()=>{ console.log('server running...'); })
3. 上边处理文件上传的响应需要使用formidable 插件,详见npm.js formidable
安装formidable
npm i formidable@v2