express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架,官方网址:https://www.expressjs. com.cn/
简单来说,express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用(HTTP 服务)
一、初体验
// 1、导入 express 模块
const express = require('express')
// 2、创建应用对象
const app = express()
// 3、创建路由
app.get('/home', (req,res) => {
res.end('hello express')
})
// 4、监听端口,启动服务
app.listen(3000, () => {
console.log('服务启动成功....')
})
命令行执行该脚本
node <文件名>
# 或者
nodemon <文件名>
二、路由
1、什么是路由
官方定义: 路由确定了应用程序如何响应客户端对特定端点的请求
2、路由的使用
一个路由的组成有 请求方法 , 路径 和 回调函数 组成
express 中提供了一系列方法,可以很方便的使用路由,使用格式如下:
app.<method>(path,callback)
// 引入 express 模块
const express = require('express')
// 创建 express 应用
const app = express()
// 创建路由
app.get('/home', (req,res) => {
res.end('hello express')
})
app.get('/', (req,res) => {
res.end('home')
})
app.post('/login', (req,res) => {
res.end('login')
})
// all 表示匹配所有方法:get、post等
// 只匹配路径,
app.all('/test', (req,res) => {
res.end('test')
})
// 路径匹配不上时,就匹配 *
app.all('*', (req,res) => {
res.end('404 not Found')
})
// 监听端口
app.listen(3000, () => {
console.log('服务已经启动....')
})
3、获取请求参数
express 框架封装了一些 API 来方便获取请求报文中的数据,并且兼容原生 HTTP 模块的获取方式
// http://127.0.0.1:3000/request?name=zs&age18
const express = require('express')
const app = express()
app.get('/request', (req,res) => {
// console.log(req.method) // GET
// console.log(req.url) // /request?name=zs&age18
// console.log(req.httpVersion) // 1.1
// console.log(req.headers) // {}
// express 操作
// console.log(req.path) // /request
// console.log(req.query) // { name: 'zs', age18: '' }
// console.log(req.ip) // ::ffff:127.0.0.1
// 获取请求头
console.log(req.get('host')) // 127.0.0.1:3000
res.end('request')
})
app.listen(3000, () => {
console.log('服务已经启动')
})
4、获取路由参数
路由参数指的是 URL 路径中的参数(数据)
通过 req.params 获取
app.get('/:id.html', (req,res) => {
console.log(req.params.id)
res.setHeader('content-type', 'text/html;charset=utf-8')
res.end(`商品详情---商品id为${req.params.id}`)
})
5、路由参数练习
访问 http://127.0.0.1:3000/singer/id.html,根据 id 不同显示不同人员对信息
singers.json
{
"singers": [
{
"singer_name":"周杰伦",
"singer_pic":"https://tse4-mm.cn.bing.net/th/id/OIP-C.MBdWohb239f-DDL1hTOD1gHaFj?rs=1&pid=ImgDetMain",
"other_name":"Jay Chou",
"singer_id": 4342,
"id": 1
},
{
"singer_name":"林俊杰",
"singer_pic":"https://ts1.cn.mm.bing.net/th/id/R-C.88bd5be6d420729a8c2522cea088dbba?rik=k6VZw%2bb%2bOra%2beA&riu=http%3a%2f%2fpic.baike.soso.com%2fp%2f20130105%2f20130105232231-1799387588.jpg&ehk=YSa6clxTqIwRP5wPGj6JGXPfHacZlaS9wPieFXYoi0U%3d&risl=&pid=ImgRaw&r=0",
"other_name":"JJ Lin",
"singer_id": 233,
"id": 2
},
{
"singer_name":"邓紫棋",
"singer_pic":"https://ts1.cn.mm.bing.net/th/id/R-C.1b542de55345409e9d43876f6ee1cb63?rik=SSmPaHF5CsAUxA&riu=http%3a%2f%2fn.sinaimg.cn%2fsinakd20110%2f120%2fw1080h1440%2f20220718%2f8dc6-218cc21c8f91db792bf3166bc3da4de4.jpg&ehk=gossNEDToapx2%2f7lhVLnyfLQta8zoXHzwgqVnDpSR%2fg%3d&risl=&pid=ImgRaw&r=0",
"other_name":"Cloria Tang",
"singer_id": 343,
"id": 3
}
]
}
路由参数练习.js
const express = require('express')
const { singers } = require('./singers.json')
const app = express()
app.get('/singer/:id.html', (req, res) => {
let singer = singers.find(item => {
return item.id === Number(req.params.id)
})
if(!singer) {
res.statusCode = 404
res.end('404 not Found')
return
}
res.end(`
<!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>${singer.singer_name}</h1>
<img src="${singer.singer_pic}" alt="" width="200">
</body>
</html>
`)
})
app.listen(3000, () => {
console.log('服务已经启动....')
})
6、设置一般响应
const express = require('express')
const app = express()
app.get('/response', (req, res) => {
// 原生响应
// 设置状态码
// res.statusCode = 404
// // 设置状态信息
// res.statusMessage = 'not Found'
// // 设置请求头
// res.setHeader('xxx', 'yyy')
// // 设置响应体
// // res.write('hello express')、
// // 设置响应体
// res.send('你好')
// 设置响应体
// res.end('response')
// express 响应
// 设置状态码
// res.status(500)
// // 设置请求头
// res.set('aaa', 'bbb')
// // 设置响应体(会自动设置 Content-Type)
// res.send('你好 express')
// express 设置响应也可以链式调用
res.status(500).set('aaa','bbb').send('你好啊 express')
})
app.listen(3000, () => {
console.log('服务已经启动....')
})
7、设置其他类型
const express = require('express')
const app = express()
app.get('/other', (req, res) => {
// 其他响应
// 设置重定向 302
// res.redirect('https://www.jd.com/')
// 下载响应(里面放绝对路径)
// res.download(__dirname+'/package.json')
// JSON 响应,会自动设置 json 对应的响应头
// res.json({
// name: 'zs',
// age: 18
// })
// 响应文件内容
res.sendFile(__dirname + '/index.html')
// 这里的响应,不需要再用 res.end() 结束
})
app.listen(3000, () => {
console.log('服务已经启动成功....')
})
三、express 中间件
1、概念
中间件Middleware
本质是一个回调函数。
中间件函数可以像路由回调一样访问 请求对象、响应对象。
好像是 vue2 中的路由守卫
2、作用
使用函数封装公共操作,简化代码
3、类型
- 全局中间件(类似于全局路由守卫)
每次访问路由,都会先经过中间件,然后经过路由 - 路由中间件(类似于独享路由守卫)
访问具体的路由时,会先经过 路由中间件,再经过路由
以坐火车流程来举例,全局中间件就好像是 进站口,每个人都需要经过进站口,路由中间件 就好像是某一趟车的闸机口,只有这一趟车的人才会经过这个闸机口。
4、全局中间件
每一个请求 到达服务端之后 都会执行全局中间件函数
- 首先定义中间件函数 fn,接收3个参数:req、res、next(函数)
- 通过 app.user(fn) 应用中间件
let recordMiddleware = function(request,response,next){
//实现功能代码
//.....
//执行next函数(当如果希望执行完中间件函数之后,仍然继续执行路由中的回调函数,必须调用next)
next();
}
app.use(recordMiddleware)
app.use(recordMiddleware2)
const express = require('express')
const fs = require('fs')
const path = require('path')
const app = express()
// 声明中间件函数
function recordMiddleware(req, res, next) {
// 获取 url、ip
let { url, ip } = req
// 将信息保存在文件 access.log 中
fs.appendFileSync(path.resolve(__dirname, './access.log'), `${url} ${ip}\r\n`)
// 调用 next(这里如果不调用 next,后续的路由回调不会正常执行)
next()
}
// 使用中间件函数
app.use(recordMiddleware)
app.get('/home', (req, res) => {
res.end('home')
})
app.get('/admin', (req, res) => {
res.end('admin')
})
app.all('*', (req, res) => {
res.end('404 not Found')
})
app.listen(3000, () => {
console.log('服务已经启动...')
})
5、路由中间件
如果 只需要对某一些路由进行功能封装 ,则就需要路由中间件
app.get('/路径','中间件函数',(request,response)=>{ });
/*
针对 /admin /setting 的请求,要求 URL 携带 code= 521 参数,如未携带提示 暗号错误
*/
const express = require('express')
const app = express()
// 定义路由中间件
const checkCodeMiddleware = (req, res, next) => {
if(req.query.code === '521') {
next()
} else {
res.send('暗号错误')
}
}
// 在需要校验的路由上,添加路由中间价
app.get('/admin', checkCodeMiddleware, (req, res) => {
res.send('admin')
})
app.get('/setting', checkCodeMiddleware, (req, res) => {
res.send('setting')
})
app.listen(3000, () => {
console.log('服务已经启动.....')
})
6、讲台资源中间件
预习:nodejs
// 创建一个 HTTP 服务,端口 9000,满足如下需求:
// GET /index.html 响应 page/index.html 的文件内容
// GET /css/app.css 响应 page/css/app.css 的文件内容
// GET /images/logo.png 响应 page/images/logo.png 的文件内容
const http = require('http')
// 加载 fs 模块处理文件
const fs = require('fs')
// 加载 path 模块处理文件名、后缀
const path = require('path')
// 常用的 请求头
const mime = {
html: 'text/html',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json'
}
const server = http.createServer((request, response) => {
let { pathname } = new URL(request.url, 'http://127.0.0.1:9000')
if(request.method !== 'GET') {
response.statusCode = 405
response.end('<h1>405 Method Not Allowed</h1>')
}
fs.readFile(`${__dirname}/page${pathname}`, (err, data) => {
// 读取文件失败后的处理
if(err) {
response.setHeader('content-type','text/html;charset=utf-8')
switch (err.code) {
case 'ENOENT':
response.statusCode = 404
response.end('<h1>404 NotFound</h1>')
break;
case 'EPERM':
response.statusCode = 403
response.end('<h1>403 Forbidden</h1>')
break;
default:
response.statusCode = 500
response.end('<h1>500 Internal Server Error</h1>')
break;
}
return
}
// 获取文件后缀,如:html
let ext = path.extname(pathname).slice(1)
if(ext === 'html') {
response.setHeader('content-type', `${mime[ext]};charset=utf-8`)
} else {
response.setHeader('content-type', mime[ext])
}
response.end(data)
})
})
server.listen('9000', () => {
console.log('服务已经启动....')
})
express 内置处理静态资源的中间件
const express = require('express')
const app = express()
// express 有内置的静态资源中间件,只需要设置一下资源目录
app.use(express.static(__dirname + '/public'))
//如果访问的内容经常变化,还是需要设置路由
//但是,在这里有一个问题,如果public目录下有index.html文件,单独也有index.html的路由,
//则谁书写在前,优先执行谁
app.get('/home', (req,res) => {
res.end('home')
})
app.listen(3000, () => {
console.log('服务启动成功....')
})
注意事项:
- index.html 文件为默认打开的资源
- 如果静态资源与路由规则同时匹配,谁先匹配谁就响应
- 路由响应动态资源,静态资源中间件响应静态资源
7、获取请求体数据 body-parser 中间件
第一步:安装
npm i body-parser
第二步:导入 body-parser 包
const bodyParser = require('body-parser');
//处理 querystring 格式的请求体
let urlParser = bodyParser.urlencoded({extended:false}));
//处理 JSON 格式的请求体
let jsonParser = bodyParser.json()
// 第四步:设置路由中间件,然后使用 request.body 来获取请求体数据
app.post('/login', urlParser, (request,response)=>{ //获取请求体数据 //console.log(request.body); //用户名 console.log(request.body.username); //密码 console.log(request.body.userpass); response.send('获取请求体数据'); });
获取到的请求体数据:[Object: null prototype] { username: 'admin', userpass: '123456' }
四、Router
1、概念
express 中的 Router 是一个完整的中间件和路由系统,可以看作是一个小型的 app 对象。
2、作用
对路由进行模块化,更好的管理路由。
3、使用
创建独立的 JS 文件(homeRouter.js)
// 1、导入 express
const express = require('express')
// 2、创建路由器对象
const router = express.Router()
// 3、在 router 对象上添加路由
router.get('/home', (req, res) => {
res.send('前台首页')
})
router.get('/search', (req, res) => {
res.send('搜索首页')
})
// 暴露 router 对象
module.exports = router
主文件 index.js
const express = require('express')
// 引入子路由文件
const adminRouter = require('./routes/adminRouter')
const homeRouter = require('./routes/homeRouter')
const app = express()
// 设置和使用中间件
app.use(adminRouter)
app.use(homeRouter)
// app.get('/home', (req, res) => {
// res.send('前台首页')
// })
// app.get('/search', (req, res) => {
// res.send('搜索页面')
// })
// app.get('/admin', (req, res) => {
// res.send('后台首页')
// })
// app.get('/setting', (req, res) => {
// res.send('设置页面')
// })
app.all('*', (req, res) => {
res.send('404 Not Found')
})
app.listen(3000, () => {
console.log('服务已经启动....')
})
五、EJS 模板引擎
1、什么是模板引擎
模板引擎是分离 用户界面和业务数据 的一种技术。
2、什么是EJS
EJS 是一个高效的 Javascript 的模板引擎。
官网: https://ejs.co/
中文站:https://ejs.bootcss.com/
3、初体验
下载安装 EJS
npm i ejs --save
代码:
const ejs = require('ejs')
const fs = require('fs')
const country = '中国'
// const str = `我爱你 ${country}`
// const str = ejs.render('我爱你 <%= country %>', {country})
const file = fs.readFileSync(__dirname + '/01-初体验.html').toString()
const name = 'zhangsan'
const str = ejs.render(file, {country, name})
console.log(str)
01-初体验.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>
<h1>我爱你 <%= country %></h1>
<h2>来自 <%= name %></h2>
</body>
</html>
4、常用语法
执行JS代码
<% code %>
输出转义的数据到模板上<%= code %>
输出非转义的数据到模板上<%- code %>
练习:
4-1、列表渲染
02-列表渲染.js
const fs = require('fs')
const ejs = require('ejs')
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧']
// 1、js 模板字符串
// let html = '<ul>'
// xiyou.forEach(item => {
// html += `<li>${item}</li>`
// })
// html += '</ul>'
// 2、ejs 直接拼标签
// let html = ejs.render(`<ul>
// <% xiyou.forEach(item => { %>
// <li> <%= item %> </li>
// <% }) %>
// </ul>`, {xiyou})
// 3、读取 html 文件,ejs 将数据传给 html
let file = fs.readFileSync(__dirname + '/02-列表渲染.html').toString()
let html = ejs.render(file, { xiyou })
console.log(html)
02-列表渲染.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>
<ul>
<% xiyou.forEach(item => { %>
<li><%= item %></li>
<% }) %>
</ul>
</body>
</html>
4-2、条件渲染
/*
通过 isLogin 决定最终的输出内容
true 输出 <span>欢迎回来</span>
flase 输入 <button>登录</button> <button>注册</button>
*/
const ejs = require('ejs')
let isLogin = false
// let html
// // 原生 js
// if(isLogin) {
// html = `<span>欢迎回来</span>`
// } else {
// html = `<button>登录</button> <button>注册</button>`
// }
let html = ejs.render(`
<% if(isLogin) { %>
<span>欢迎回来</span>
<% } else { %>
<button>登录</button> <button>注册</button>
<% } %>
`, {isLogin})
console.log(html)
5、express 中使用 ejs
- 创建一个目录(views)用来存放模板(以 ejs 结尾)
- 路由上使用
const express = require('express')
const path = require('path')
const app = express()
// 1、设置模板引擎
app.set('view engine','ejs');
app.engine('ejs', require('ejs').__express);
// 2、设置模板文件存放位置, 模板文件:具有模板语法内容的文件
app.set('views', path.resolve(__dirname, './views'))
app.get('/home', (req, res) => {
let title = '使用 ejs 模板渲染'
// 3、render 响应
// res.render('模板的文件名', '书库')
res.render('home', { title })
// res.send('你好啊')
})
app.listen(3000, () => {
console.log('服务已经启动过成功')
})
6、express- generator 工具
它是 express 应用程序生成器,通过 express- generator 可以快速创建一个应用的骨架(类似与 vue 中的 vue-cli)。
6-1、下载
npm install -g express-generator
6-2、使用
express -e 目录名XXX
创建目录 XXX,这个目录 添加了 ejs 模板引擎的支持。