Express框架
目标
1)Express是一个基于Node平台的web应用开发框架,它提供了一系列的强大特性,帮助你创建各种Web网站应用。使用原生JS代码写起来比较复杂,比较底层的;比如实现路由功能,需要对请求地址进行解析,还要进行各种判断,代码乱,不易阅读;再比如,实现静态资源访问功能,还要使用文件读取模块,对文件内容读取,还要设计响应内容类型,但实际和网站本身的业务逻辑没有关系;还有,接收post请求参数的代码,需要对请求对象添加事件,手动拼接请求参数,对请求参数的格式进行转化,都是复杂的,并且还是和和业务逻辑没有关系;原生JS实现网站应用比较困难,express就出现了
2)这个是nodejs的第三方模块-使用 npm install express 命令进行下载。企业中创建web应用的标准
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路由'); }); |
app.on('request', (req, res) => { // 获取GET参数 let { query} = url.parse(req.url, true); // 获取POST参数 let postData = ''; 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); }) |
使用Express框架 创建web服务器及其简单,调用express模块 返回的函数即可。
命令行下载: npm install express //1, 引入Express框架,返回值是一个方法,通过调用这个方法,就可以创建网站服务器,就不用HTTP模块和调用createserver()方法 const express = require('express'); //2, 使用框架创建web服务器 + 监听端口 = 向外提供服务 const app = express();
// 4, 当客户端以get方式访问/路由时, 服务器要创建路由来响应客户端的请求;如何创建路由,和第三方模块 router是一样的 app.get(‘默认访问地址/’,请求处理函数,两个参数分别为 请求对象和 响应对象) 用来接受 get 请求 app.get('/', (req, res) => { // 不再是res.end(), 对客户端做出响应 send()方法会根据内容的类型自动设置请求头 // send() 内部回检测响应内容的类型;会自动设置HTTP状态码;会帮我们自动设置响应的内容类型以及 编码 res.send('Hello Express'); // });
//在定义一个路由,当访问 / list 的时候,响应一个其他内容;send 内部 还可以传递Json 对象 app.get('/list', (req, res) => { res.send({name: '张三', age: 20}) }) //3, 程序监听3000端口 app.listen(3000); |
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); });
|
- app.use 匹配所有的请求方式,可以直接传入请求处理函数,代表接收所有的请求,只要客户端发来请求,就可以匹配到当前中间件
中间件是有顺序的,所以,中间件必须定义在其他前边;否则其他中间件匹配到了这个请求,有没有将权力交给下一个中间件,也是匹配不到这个中间件的
app.use((req, res, next) => { console.log(req.url); next(); });
app.use('/admin', (req, res, next) => { console.log(req.url); next(); });
1. 路由保护,客户端在访问 需要登录的页面时,可以先使用中间件判断用户登录状态,用户如果未登录,则拦截请求,直接响应, 禁止用户进入需要登录的页面。
// 定义一个路由:要访问必须要先登录可以的 |
2. 网站维护公告,在所有路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,网站正在维护中。
// 网站公告,比如在凌晨 6:00 -12:00 要维护,不想要用户访问这个网站,要定义在所有的路由的前边,没有调用 next(), 请求到这里就截止了 app.use((req, res, next) => { res.send('当前网站正在维护,请在其他时间段访问...') }) |
3. 自定义404页面,用户访问路径不存在时,同时用户访问的页面不存在,当所有用户访问的上边所有的路由不存在,才会响应给用户,所以定义在所有路由最后边,不会调用next();status(404)更改状态码,为客户端响应404状态码以及提示信息
在程序执行的过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败,错误处理中间件是一个集中处理错误的地方。
想要出错以后,还能继续运行,需要捕获错误,加入错误处理;程序错误分为两种:
如何处理错误呢?在每一个会出错的地方进行判断,但是代码太多!所以提供了错误处理中间件;
只能捕获到 同步代码错误!!!,异步需要手动触发,调用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('网站服务器启动成功'); |
在node.js中,异步API的错误信息都是通过 回调函数 获取的,支持Promise对象的异步API 发生错误可以通过catch方法捕获。 异步函数执行如果发生错误要如何捕获错误呢?
try catch 可以捕获 异步函数以及其他同步代码 在执行过程中发生的错误,但是 不能捕获其他类型的API发生的错误
app.get("/", async (req, res, next) => { // 如果程序没有错误,跳到try,catch外;如果程序有错误,会执行catch里边的代码,里边的参数就是错误信息,可以调用next方法,手动触发错误处理中间件;try()里边的代码,是从数据块中查询数据,如果查询失败,就跳转catch,执行并将错误信息传给错误处理中间件; try { await User.find({ name: '张三'}) }catch(ex) { next(ex); // 调用next() 触发错误处理中间件 } }); |
命令行已经不报错了,程序就可以继续执行;增加了我们程序的健壮性 |
虽然已经可以通过 app.get(), app.post()方法创建路由了,但是在一般情况下,路由的数量是非常多的,如果将所有的放在同一文件中,非常可怕,所以express提供了模块化,进行分类,不同的类型路由放在不同的模块中,方便管理。
例如:博客网站:用户看的文章列表,详情页面;管理员看的文章发布,管理页面等。设置不同的路由进行分别管理
const express = require('express') // 引入框架,返回express方法,直接调用或者使用他下边的其他方法;比如 express.Router用来创建路由 const home = express.Router();// 创建路由对象 app.use('/home', home); // 将路由和请求路径进行匹配;当客户端访问什么请求路径时/home',才能使用当前路由来处理,用app.use()方法来匹配 home;代码中并没有请求处理函数,请求来了以后,在哪里处理呢?具体请求处理再二级路由中完成 home.get('/index', () => { // 在home路由下的get()方法继续创建路由,访问:/home/index 二级路由 res.send('欢迎来到博客展示页面'); // /home/index }); |
// home.js |
// admin,js |
// app.js |
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"} }); |
|
Express中接收 post请求参数 需要借助第三方包 body-parser。Npm i body-parser下载
const bodyParser = 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方法 |
传递和接受 get 请求参数,还有另一种方式; 路由参数;可以让请求地址看起来美观,路由代码容易阅读;更容易看出传了那些参数;:id 是一个占位符,请求当前路由,要传递一个id 作为参数,不是实际的参数
app.get('/find/:id', (req, res) => { console.log(req.params); // {id: 123} req.params 获取参数 }); localhost:3000/find/123 // 请求参数/?id = 123 |
通过Express内置的 express.static可以方便地托管静态文件,例如img、CSS、JavaScript 文件等。
app.use(express.static('public')); express.static(‘参数:静态资源存放的目录’);调用传给app.use()中间件,拦截所有请求,将请求交给express.static()这个方法处理,并且将静态资源目录告诉express.static()方法;方法内部判断客户端发来的请求,如果是静态资源请求,直接响应给客户端,终止请求;如果不是再方法内部调用next()将请求控制权交给下一个中间件;开启静态资源访问功能后,就可以,public 目录下面的文件就可以通过以下方式访问了。
访问:http://localhost:3000/static/images/1.jpg
// 告诉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'); }); |
不同的页面中,总会有公共数据,代码中如何查询公共数据呢?
app.locals.users = [{ name: '张三', age: 20 },{ name: '李四', age: 20 }] |