目录
一. express的安装和基本使用
1. express的安装
npm install express --save
具体可按照express官方文档中的安装方法进行安装>_<
2. express的基本使用
// 引入express框架
const express = require('express');
//创建网站服务器
const app = express();
//路由清单
app.get('/',function (req,res) {
//返回数据时使用send方法
res.send('首页')
})
app.get('/student' ,function (req,res) {
res.send('学生页面');
})
app.get('/teacher',function (req,res) {
res.send('教师页面');
})
// 监听端口
app.listen(3000);
console.log("Server start at 3000 port")
二.express中间件
1. 什么是中间件
1.1 中间件的基本介绍
也可以理解为匹配路由之前或者匹配路由完成时所作的一系列的操作,我们可以把他叫做中间件.
中间件的功能是可以访问请求对象,响应对象和应用程序请求-响应周期中下一个中间件的功能.下一个中间件功能通常由名为next的变量表示.
中间件的功能:
- 执行任何代码。
- 更改请求和响应对象。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
中间件的方法是由Express提供的, 负责拦截请求, 请求处理函数是有开发人员提供的, 负责处理请求.
1.2 中间件的结构
基本语法:
app.use([path , ] callback [, callback...])
参数:
path: 可选,是路由的url,如果省略,则会匹配到所有路径;
callback:中间件函数,当路由匹配成功时执行函数,该函数接受三个参数;
callback(request,response,next);
request:http请求对象;
response:http响应对象
next: 处理完则交给下一下函数,若不调用,则处理到此处就不在继续执行
示例:
app.use('/'.function(req,res,next){
})
2. 尾函数next()
如果中间件里不调用next函数,整个请求响应流程就会中断,不会再继续往后面执行.
示例:
// 引入express框架
const express = require('express');
//创建网站服务器
const app = express();
app.use('/',function (req,res,next) {
console.log(111);
console.log(222);
next(); //如果不调用next,将不会执行下一个中间件,不会打印333,444
})
app.use('/',function (req,res,next) {
console.log(333);
console.log(444);
})
// 监听端口
app.listen(3000);
console.log("Server start at 3000 port")
调用next的打印结果:
不调用next的打印结果:
3. 下一个中间件执行完毕
中间件类似于过滤器,用于在客户端和应用程序之间处理请求和响应的方法。中间件的执行类似剥洋葱,但并非一层层的执行,而是以 next 为分界,先执行本层 next 之前的部分,当下一层中间件执行完毕后再执行本层 next 之后的部分。
调用尾函数就会执行下一个中间件,下一个执行完毕后回来继续执行自己的函数
示例:
app.use('/',function (req,res,next) {
console.log(111);
console.log(222);
next();
console.log(555);
})
app.use('/',function (req,res,next) {
console.log(333);
next();
console.log(444);
})
app.use('/',function (req,res,next) {
console.log(666);
next();
console.log(777);
})
打印结果
// 111
// 222
// 333
// 666
// 777
// 444
// 555
配色有丑到我,凑活着看吧,大哭呜呜呜........
4. 中间件的分类
4.1 内置中间件
1. express.static()\
express.static() 用来处理内置静态文件
// 推荐使用绝对路径
app.use(express.static(path.join(__dirname,'public')))
我们也可以给静态资源目录制定一个虚拟路径
// 第一个参数就是给静态资源目录制定的虚拟路径
app.use('/static',express.static(path.join(__dirname,'public')))
2. express.json()
这个内置中间件和下面的一个 express.urlencoded () 都是用来处理post 请求参数的,post的请求参数有两种,一种是表单方式,另外一种弄是json形式的.
处理 json 形式的传参
内置的 json 中间件负s责把带有 JSON 的请求中(即 content - type = 'application/json') 的数据提取出来,它基于body-parser
app.use(express.json());
3. express.urlencoded ()
处理表单方式的传参
urlencoded 负责将通过 urlencoded 发送请求(即Content-Type=‘application/x-www-form-urlencoded’
)的数据提取出来,它基于 body-praser 经过此中间件后, req.body为解析后的json串.
示例:
const express = require("express")
const bodyparser = require("body-parser")
const app = express()
// 处理post请求表单传参方式的数据
app.use(express.urlencoded())
// 处理post 请求json 传参方式的数据
app.use( express.json())
app.use(function(req,res,next){
console.log(req.body)
res.send("首页")
})
app.listen(3000, function(){
console.log("Server start at 3000 port")
})
4.2 自定义中间件
在上面中间件结构中,我们知道了,中间件使用时的第二个参数是一个Function,然而,要自定义一个中间件,就是倒腾一番这个Function。
这个function总共有三个参数(req,res,next);
当每个请求到达服务器时,nodejs会为请求创建一个请求对象(request),该请求对象包含客户端提交上来的数据。同时也会创建一个响应对象(response),响应对象主要负责将服务器的数据响应到客户端。而最后一个参数next是一个方法,因为一个应用中可以使用多个中间件,而要想运行下一个中间件,那么上一个中间件必须运行next()。
app.use(function (req, res, next) {
//处理代码
})
4.3 第三方中间件
有关第三方中间件,介绍几个比较重要和常用的:
-
body-parser :解析body中的数据,并将其保存为Request对象的body属性。
-
cookie-parser :解析客户端cookie中的数据,并将其保存为Request对象的cookie属性
-
express-session :解析服务端生成的sessionid对应的session数据,并将其保存为Request对象的session属性
const express = require("express")
const bodyparser = require("body-parser")
const app = express()
app.use(bodyparser.urlencoded())
app.use(bodyparser.json())
app.post("/json", (req,res,next) => {
// console.log(req)
// 获取post 请求的数据
console.log(req.body)
res.send({name:"pp",age:18})
})
app.listen(3001, function(){
console.log("Server start at 3001 port")
})
4.4 错误中间件
在程序执行过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败,错误处理中间件是一个集中处理错误的地方
app.get('/' ,function (req,res,next) {
// 手动抛出错误
throw new Error('服务器出现错误');
})
app.use(function(err, req,res,next){
res.status(500).send(err.message)
})
上面的错误例子是同步的错误, 如果是异步语句发生错误要调用next()并且传入错误参数
app.get('/',function (req,res,next) {
fs.readFile('./index.html','utf8',(err,data) => {
if(err) {
next(err)
}else {
res.send(data);
}
})
})
app.use(function(err, req,res,next){
res.status(500).send(err.message)
})
5.中间件的应用
5.1 路由保护
客户端在访问登录页面的时候,可以先使用中间件判断用户登录状态, 用户如果未登录,则拦截请求, 直接响应, 禁止用户进入登录页面
我们可能会有对公页面,在下面的例子中设置了根目录和学生目录两个目录,如果不使用中间件,则每一次都需要判断输入的token和设置的token 是否相等,代码会和繁琐,如下:
app.get('/',function (req,res,next) {
console.log(req.query)
if(req.query.token == 'why') {
res.send('登录成功 当前目录为根目录')
}else {
res.send('未登录,请先登录');
}
})
app.get('/student',function (req,res,next) {
console.log(req.query)
if(req.query.token == 'why') {
res.send('登录成功 当前目录为学生目录')
}else {
res.send('未登录,请先登录');
}
})
使用浏览器访问我们的服务器:
如果输入的token的值不是why,则结果如下:
使用中间件则会使我们的代码精简很多,如下:
app.use((req,res,next) => {
if(req.query.token == 'why') {
next();
}else {
res.send('未登录,请先登录');
}
})
app.get('/',function (req,res,next) {
res.send('登录成功 当前目录为根目录')
})
app.get('/student',function (req,res,next) {
res.send('登录成功 当前目录为学生目录')
})
5.2 网站维护公告
在所有路由之前最上面定义接受所有组件的中间件, 直接为客户端响应,网站正在维护中
app.use((req,res,next) => {
let hour = new Date().getHours();
if(22 <= hour && hour <= 24) {
res.send('网站正在维护中.....')
}
next();
})
5.3 自定义404 页面
在所有路由后面定义中间件, 处理404
app.use(function(req,res,next){
res.status(404).send("404, 你访问的页面不存在")
})