Express框架
目标
- 能够使用Express创建web服务器
- 能够使用Express处理请求参数
- 能够使用Express处理静态资源
- 能够使用中间件处理请求
- 能够在Express中集成art-template模板引擎
- Express框架简介及初体验
- Express框架请求处理
- express-art-template模板引擎
- Express中间件
1. Express框架简介及初体验
1.1 Express框架是什么?
1)Express是一个基于Node平台的web应用开发框架,它提供了一系列的强大特性,帮助你创建各种Web网站应用。使用原生JS代码写起来比较复杂,比较底层的;比如实现路由功能,需要对请求地址进行解析,还要进行各种判断,代码乱,不易阅读;再比如,实现静态资源访问功能,还要使用文件读取模块,对文件内容读取,还要设计响应内容类型,但实际和网站本身的业务逻辑没有关系;还有,接收post请求参数的代码,需要对请求对象添加事件,手动拼接请求参数,对请求参数的格式进行转化,都是复杂的,并且还是和和业务逻辑没有关系;原生JS实现网站应用比较困难,express就出现了
2)这个是nodejs的第三方模块-使用 npm install express 命令进行下载。企业中创建web应用的标准
1.2 Express框架特性
- 提供了方便简洁的路由定义方式:router第三方模块 ,其实就是从express框架中抽取出来的
- 对获取HTTP请求参数进行了简化处理:不用再转换格式,直接拿到对象类型
- 对模板引擎支持程度高,方便渲染动态HTML页面;
- 提供了中间件(对请求的拦截)机制有效控制HTTP请求
- 拥有大量第三方中间件对功能进行扩展:非常少的代码,做同样的事情
1.3原生Node.js与Express框架对比 之 路由
app.on('request', (req,res) => { //获取客户端的请求路径 let {pathname} =url.parse(req.url); //对请求路径进行判断 不同的路径地址响应不同的内容 if (pathname== '/' ||pathname== 'index') { res.end('欢迎来到首页'); }else if (pathname== '/list') { res.end('欢迎来到列表页'); }else if (pathname== '/about') { res.end('欢迎来到关于我们页面') }else { res.end('抱歉,您访问的页面出游了'); } }); |
//当客户端以get方式访问/时 app.get('/', (req,res) => { //对客户端做出响应 res.send('Hello Express'); }); //当客户端以post方式访问/add路由时 app.post('/add', (req,res) => { res.send('使用post方式请求了/add路由'); }); |
1.4原生Node.js与Express框架对比 之 获取请求参数
app.on('request', (req,res) => { //获取GET参数 let{query} =url.parse(req.url, true); //获取POST参数 letpostData= ''; req.on('data', (chunk) =>{ postData+=chunk; }); req.on('end', () =>{ console.log(querystring.parse(postData) })); }); |
app.get('/', (req,res) => { //获取GET参数req.query console.log(req.query); }); app.post('/', (req,res) => { //获取POST参数req.body console.log(req.body); }) |
1.5 Express初体验
使用Express框架 创建web服务器及其简单,调用express模块 返回的函数即可。
命令行下载:npm install express //1,引入Express框架,返回值是一个方法,通过调用这个方法,就可以创建网站服务器,就不用HTTP模块和调用createserver()方法 constexpress=require('express'); //2,使用框架创建web服务器+监听端口=向外提供服务 constapp=express();
// 4,当客户端以get方式访问/路由时,服务器要创建路由来响应客户端的请求;如何创建路由,和第三方模块router是一样的app.get(‘默认访问地址/’,请求处理函数,两个参数分别为 请求对象和 响应对象)用来接受get请求 app.get('/', (req,res) => { //不再是res.end(),对客户端做出响应send()方法会根据内容的类型自动设置请求头 // send()内部回检测响应内容的类型;会自动设置HTTP状态码;会帮我们自动设置响应的内容类型以及 编码 res.send('Hello Express'); // <h2>Hello Express</h2> {say: 'hello'} });
//在定义一个路由,当访问/ list的时候,响应一个其他内容;send内部 还可以传递Json对象 app.get('/list', (req, res) => { res.send({name: '张三', age: 20}) }) //3,程序监听3000端口 app.listen(3000); |



2.中间件
2.1什么是中间件
1) 中间件就是 一堆方法,可以 接收客户端发来的请求、可以对请求做出响应,也可以将请求继续交给下一个中间件继续处理。专门接受请求处理请求的
下图:中间件处理请求的过程:
图两边是当客户端浏览器, 向中间的服务器发送请求
当浏览器发送请求,服务器可以使用中间件 接受这个请求进行处理,直接对客户端做出响应,或者交给下一个中间件继续处理,有下一个 中间件对客户端浏览器做出相应;
中间件好处:可以对复杂的请求处理逻辑,进行分开处理,也可以再请求到到指定路由之前做一些验证:比如查看用户是不是登录,如果登录,在向下继续执行

2) 中间件主要由两部分构成,中间件方法以及请求处理函数。 中间件方法 由Express框架提供,负责拦截请求,请求处理函数 由开发人员提供,负责处理请求。
app.get('请求路径', '处理函数') //接收并处理get请求 app.post('请求路径', '处理函数') //接收并处理post请求 |
3) 可以针对同一个请求设置 多个中间件,对同一个请求进行多次处理。
默认情况下,请求从上到下依次匹配中间件,一旦匹配成功,终止匹配。
可以调用第三个参数next()方法将请求的控制权交给下一个中间件,直到遇到结束请求的中间件
app.get('/request', (req,res,next) => { req.name= "zhangsan"; next(); }); | app.get('/request', (req,res) => { res.send(req.name); });
|

![]()
2.2 app.use中间件用法
- app.use 匹配所有的请求方式,可以直接传入请求处理函数,代表接收所有的请求,只要客户端发来请求,就可以匹配到当前中间件
中间件是有顺序的,所以,中间件必须定义在其他前边;否则其他中间件匹配到了这个请求,有没有将权力交给下一个中间件,也是匹配不到这个中间件的
app.use((req, res, next) => { console.log(req.url); next(); });
- app.use 第一个参数 也可以传入请求地址,代表不论什么请求方式,只要是这个请求地址就接收这个请求。
app.use('/admin', (req, res, next) => { console.log(req.url); next(); });


2.3中间件应用
1. 路由保护,客户端在访问 需要登录的页面时,可以先使用中间件判断用户登录状态,用户如果未登录,则拦截请求,直接响应, 禁止用户进入需要登录的页面。
//定义一个路由:要访问必须要先登录可以的
|
2. 网站维护公告,在所有路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,网站正在维护中。
// 网站公告,比如在凌晨 6:00 -12:00 要维护,不想要用户访问这个网站,要定义在所有的路由的前边,没有调用next(),请求到这里就截止了 app.use((req, res, next) => { res.send('当前网站正在维护,请在其他时间段访问...') }) |
3. 自定义404页面,用户访问路径不存在时,同时用户访问的页面不存在,当所有用户访问的上边所有的路由不存在,才会响应给用户,所以定义在所有路由最后边,不会调用next();status(404)更改状态码,为客户端响应404状态码以及提示信息


2.4错误处理中间件
在程序执行的过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败,错误处理中间件是一个集中处理错误的地方。
想要出错以后,还能继续运行,需要捕获错误,加入错误处理;程序错误分为两种:
- 应用逻辑错误:=bug开发阶段解决
- 读取硬盘文件/数据库连接错误:无法开发阶段预料到,在运行过程中,需要被捕获和妥善处理;
如何处理错误呢?在每一个会出错的地方进行判断,但是代码太多!所以提供了错误处理中间件;
只能捕获到 同步代码错误!!!,异步需要手动触发,调用next(0方法 当异步程序出现错误时,调用next()方法,并且将错误信息通过参数的形式传递给next()方法,即可触发错误处理中间件
有四个参数,发生错误,自动执行错误处理中间件 app.use((err,req,res,next) => { res.status(500).send('服务器发生未知错误'); }) |
如果文件读取错误,系统会把错误信息通过参数传给我们,我们对错误对象进行判断,如果它真的时错误对象,不是null,就调用next()方法,传给他,他就会触发错误处理中间件了 app.get("/", (req,res,next) => { fs.readFile("/file-does-not-exist", (err,data) => { if (err) { next(err); } }); }); |
//05,js错误处理中间件 //引入express框架+文件读取模块 const express = require('express'); const fs = require('fs'); //创建网站服务器 const app = express(); //普通的路由中间件 app.get('/index', (req, res, next) => { // throw new Error('程序发生了未知错误')抛出错误,不报错往下执行 fs.readFile('./01.js', 'utf8', (err, result) => { if (err != null) { next(err)// 传参数,代表触发中间件,不传参数,代表控制权交给下一个 }else { res.send(result) } }) // res.send('程序正常执行') }) //错误处理中间 app.use((err, req, res, next) => { res.status(500).send(err.message); }) //监听端口 app.listen(3000); console.log('网站服务器启动成功'); |
2.5捕获错误
在node.js中,异步API的错误信息都是通过回调函数获取的,支持Promise对象的异步API发生错误可以通过catch方法捕获。 异步函数执行如果发生错误要如何捕获错误呢?
try catch 可以捕获 异步函数以及其他同步代码在执行过程中发生的错误,但是不能捕获其他类型的API发生的错误
app.get("/", async (req,res,next) => { //如果程序没有错误,跳到try,catch外;如果程序有错误,会执行catch里边的代码,里边的参数就是错误信息,可以调用next方法,手动触发错误处理中间件;try()里边的代码,是从数据块中查询数据,如果查询失败,就跳转catch,执行并将错误信息传给错误处理中间件; try { awaitUser.find({name: '张三'}) }catch(ex) { next(ex);//调用next()触发错误处理中间件 }}); |
命令行已经不报错了,程序就可以继续执行;增加了我们程序的健壮性 |
3. Express请求处理
3.1构建模块化路由
虽然已经可以通过 app.get(), app.post()方法创建路由了,但是在一般情况下,路由的数量是非常多的,如果将所有的放在同一文件中,非常可怕,所以express提供了模块化,进行分类,不同的类型路由放在不同的模块中,方便管理。
例如:博客网站:用户看的文章列表,详情页面;管理员看的文章发布,管理页面等。设置不同的路由进行分别管理
constexpress=require('express')//引入框架,返回express方法,直接调用或者使用他下边的其他方法;比如express.Router用来创建路由 consthome=express.Router();//创建路由对象 app.use('/home',home); //将路由和请求路径进行匹配;当客户端访问什么请求路径时/home',才能使用当前路由来处理,用app.use()方法来匹配 home;代码中并没有请求处理函数,请求来了以后,在哪里处理呢?具体请求处理再二级路由中完成 home.get('/index', () => {//在home路由下的get()方法继续创建路由,访问:/home/index二级路由 res.send('欢迎来到博客展示页面');// /home/index }); |
|
3.2 构建模块化路由
// home.js
| // admin,js
|
// app.js
| |
3.3 GET参数的获取
Express框架中使用 req.query即可获取 GET参数,query属性下存的就是get请求参数,不在需要引入url模块,通过对请求地址进行解析来获取get请求参数了;框架内部会将GET参数 转换为 对象并返回。
//接收地址栏中问号后面的参数,客户端访问时加了请求参数 name=zhangsan&age=30 //例如: http://localhost:3000/?name=zhangsan&age=30 app.get('/', (req,res) => { console.log(req.query); //直接通过 req.query 就可以拿到?号后的请求参数了,并且已经解析成对象类型{"name": "zhangsan", "age": "30"}}); |
|
3.4 POST参数的获取
Express中接收 post请求参数 需要借助第三方包 body-parser。Npm i body-parser下载
constbodyParser=require('body-parser');//引入body-parser模块 app.use(bodyParser.urlencoded({extended: false }));//配置body-parser模块;使用app.use()这个中间件拦截所有请求,调用'body-parser'模块下边的 urlencoded() 方法,对请求进行处理,方法内部会检测当前请求中是不是包含请求参数,如果包含,就接受并转换为对象类型;然后在为req这个请求添加一个属性,属性的名字叫body;并且将参数作为值赋值给 req.body属性,最后调用next()将请求控制权交给下一个中间件 app.post('/add', (req,res) => {//接收请求 console.log(req.body);//接收post请求参数 }) |
10.js如何获取post请求参数
|
如何发送post请求,通过表单就可以:post.html
点击提交后,就提交到/add这个路由地址去了:
|
11.js app.use方法
|
3.5 Express 路由参数
传递和接受 get 请求参数,还有另一种方式; 路由参数;可以让请求地址看起来美观,路由代码容易阅读;更容易看出传了那些参数;:id是一个占位符,请求当前路由,要传递一个id作为参数,不是实际的参数
app.get('/find/:id', (req,res) => { console.log(req.params); // {id: 123}req.params获取参数 }); localhost:3000/find/123 // 请求参数/?id = 123 |
|
3.6 静态资源的处理
通过Express内置的 express.static可以方便地托管静态文件,例如img、CSS、JavaScript文件等。
app.use(express.static('public'));express.static(‘参数:静态资源存放的目录’);调用传给app.use()中间件,拦截所有请求,将请求交给express.static()这个方法处理,并且将静态资源目录告诉express.static()方法;方法内部判断客户端发来的请求,如果是静态资源请求,直接响应给客户端,终止请求;如果不是再方法内部调用next()将请求控制权交给下一个中间件;开启静态资源访问功能后,就可以,public目录下面的文件就可以通过以下方式访问了。
- http://localhost:3000/images/kitten.jpg
- http://localhost:3000/css/style.css
- http://localhost:3000/js/app.js
- http://localhost:3000/images/bg.png
- http://localhost:3000/hello.html

访问:http://localhost:3000/static/images/1.jpg
4. express-art-template模板引擎
4.1模板引擎
- 为了使 art-template模板引擎能够更好的和Express框架配合,模板引擎官方在原art-template模板引擎的基础上封装了express-art-template。
- 使用 npm install art-template express-art-template命令进行安装。
//告诉express 框架,使用的模板引擎是什么? 当渲染后缀为art的模板时 使用express-art-template; app.engine('art',require('express-art-template')); //设置模板存放目录 app.set对express框架进行配置 app.set('views',path.join(__dirname, 'views')); //渲染模板时不写后缀 默认拼接art后缀 app.set('view engine', 'art'); app.get('/', (req,res) => { //渲染模板 res.render('index'); }); |
|
4.2 app.locals 对象
不同的页面中,总会有公共数据,代码中如何查询公共数据呢?
- 在不同页面路由中 都去查询这个相同的数据,render 将数据填充到模板中,麻烦; 只写一次,让所有能用到都而已来拿到这个数据呢?
- 将变量设置到app.locals对象下面,这个数据在所有的模板中都可以获取到。!!!
app.locals.users= [{ name: '张三', age: 20 },{ name: '李四', age: 20 }] |
|






















