1、Express框架是什么
express框架是一个基于node平台的web应用开发框架,他提供了一系列的强大特性,帮助你创建各种web应用。
我们可以使用npm install express命令下载。
2、Express框架特性
- 提供了方便简洁的路由定义方式
- 对获取HTTP请求参数进行了简化处理
- 对模板引擎支持程度高,方便渲染动态HTML页面
- 提供了中间件机制有效控制HTTP请求
- 拥有大量第三方中间件对功能进行扩展
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
app.get('/',(req,res)=>{
//send方法来响应,不用使用end方法
//1、send方法会检测响应内容的类型
//2、send方法会自动设置http状态码
//3、send方法会帮我们自动设置响应的内容类型及编码
res.send('hello express')
})
app.get('/list',(req,res)=>{
console.log(req.query.name)
res.send({
name:'zhangsan',
age:34,
address:'beijing'
})
})
res.end(JSON.stringify(obj))
app.listen(3000)
console.log('网站服务器启动成功')
2.1、指定要公开的目录
const express=require('express')
const server=express()
//指定公开资源的目录
server.use('/public',express.static('./public/'))
server.listen(3000)
console.log('the website is started')
以下也是一种方式
const express=require('express')
const app=express()
// app.use('/public',express.static('./public'))
app.use('/public',express.static('./public'))
app.get('/',(req,res)=>{
res.send('hello node.js')
})
app.listen(3000)
console.log('the server is started at port 3000')
3、中间件
中间件就是一堆方法,可以接收客户端发来的请求、可以对请求做出响应,也可以将请求继续交给下一个中间件继续处理。
- 中间件主要由两部分构成,中间件方法以及请求处理函数。
- 中间件方法由Express提供,负责拦截请求,请求处理函数由开发人员提供,负责处理请求。
app.get('请求路径', '处理函数') // 接收并处理get请求
app.post('请求路径', '处理函数') // 接收并处理post请求
- 默认情况下,请求从上到下依次匹配中间件,一旦匹配成功,终止匹配。
- 可以调用next方法将请求的控制权交给下一个中间件,直到遇到结束请求的中间件。
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//请求根目录
app.get('/',(req,res)=>{
//send方法来响应,不用使用end方法
//1、send方法会检测响应内容的类型
//2、send方法会自动设置http状态码
//3、send方法会帮我们自动设置响应的内容类型及编码
res.send('hello express')
})
app.get('/request',(req,res,next)=>{
req.name='zhangsan';
next()
})
app.get('/request',(req,res)=>{
res.send(req.name)
})
app.listen(3000)
console.log('网站服务器启动成功')
4、app.use中间件
app.use匹配所有的请求方式,可以直接传入请求处理函数,代表接受所有的请求
app.use((req,res,next)=>{
console.log(req.url)
next()
})
app.use 第一个参数也可以传入请求地址,代表不论什么请求方式,只要是这个请求地址就接收这个请求。
app.use('/use',(req,res,next)=>{
console.log(req.url)
next()
})
完整案例
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//请求根目录
app.get('/',(req,res)=>{
//send方法来响应,不用使用end方法
//1、send方法会检测响应内容的类型
//2、send方法会自动设置http状态码
//3、send方法会帮我们自动设置响应的内容类型及编码
res.send('hello express')
})
//只要发生了请求就要走这个中间件
app.use((req,res,next)=>{
console.log('请求走了中间件')
next()
})
app.get('/request',(req,res,next)=>{
req.name='zhangsan';
next()
})
app.get('/request',(req,res)=>{
res.send(req.name)
})
app.listen(3000)
console.log('网站服务器启动成功')
5、中间件的应用
可以作为一个校验机制,进行路由保护
- 路由保护,客户端在访问需要登录的页面时,可以先使用中间件判断用户登录状态,用户如果未登录,则拦截请求,直接响应,禁止用户进入需要登录的页面。
- 网站维护公告,在所有路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,网站正在维护中。
- 自定义404页面
1、路由保护
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
app.use('/admin',(req,res,next)=>{
//使用一个变量模拟是否登录
let islogin=true;
if (islogin){
next()
}else{
res.send('您还没有登录,请返回登录')
}
})
app.get('/admin',(req,res)=>{
res.send('您已经登录了,可以访问当前页面')
})
app.listen(3000)
console.log('网站服务器启动成功')
2、网站维护
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
app.use((req,res,next)=>{
res.end('网站正在维护,请果断时间再来访问')
})
app.listen(3000)
console.log('网站服务器启动成功')
3、自定义404页面
这个use应该定义在文件最后,因为这样才穷尽了所有路由,才能报404错误
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
app.get('/find',(req,res)=>{
res.end('u are looking find page')
})
app.use((req,res,next)=>{
res.status(404)//设置404状态码,可以链式调用
res.end('404页面没有找到')
})
app.listen(3000)
console.log('网站服务器启动成功')
6、错误处理中间件
在程序执行的过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败。
错误处理中间件是一个集中处理错误的地方。
//错误处理中间件
app.use((err,req,res,next)=>{
res.status(500).send('服务器发生未知错误')//链式调用,500是错误的状态码
})
完整案例
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//普通路由处理中间件
app.get('/index',(req,res)=>{
//创建一个错误
throw new Error('程序发生了未知错误')
})
//错误处理中间件
app.use((err,req,res,next)=>{
res.status(500).send(err.message)//链式调用,500是错误的状态码
})
app.listen(3000)
console.log('网站服务器启动成功')
ps:只能捕获同步错误,不能捕捉异步处理中间件,只能手动触发这个错误处理中间件
当程序出现错误时,调用next()方法,并且将错误信息通过参数的形式传递给next()方法,即可触发错误处理中间件,这有就可以有程序员来处理。
app.get('/',(req,res,next)=>{
fs.readFile('./filenotexit',(err,data)=>{
if (err){
next(err)
}
})
})
实际案例
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//引入文件读取模块
const fs=require('fs')
//异步操作错误中间件
app.get('/index',(req,res,next)=>{
fs.readFile('./filenotexit','utf8',(err,result)=>{
if (err!=null){
next(err)
}else{
res.send(result)
}
})
})
//错误处理中间件
app.use((err,req,res,next)=>{
res.status(500).send(err.message)//链式调用,500是错误的状态码
})
app.listen(3000)
console.log('网站服务器启动成功')
7、try catch捕获异步函数错误
在node.js中,异步API的错误信息都是通过回调函数获取的,支持Promise对象的异步API发生错误可以通过catch方法捕获。
异步函数执行如果发生错误要如何捕获错误呢?
try catch 可以捕获异步函数以及其他同步代码在执行过程中发生的错误,但是不能其他类型的API发生的错误。
//异步操作错误中间件
app.get('/index',async (req,res)=>{
try {
await User.find({name:'张三'})
}catch (e) {
next()
}
})
实际案例
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//引入文件读取模块
const fs=require('fs')
//引入promisify模块
const promisify=require('util').promisify;
//包装自定义方法
const readFile=promisify(fs.readFile)
//异步操作错误中间件
app.get('/index',async (req,res,next)=>{
try {
await readFile('./aaa.js')
}catch (e) {
next(e)
}
})
//错误处理中间件
app.use((err,req,res,next)=>{
res.status(500).send(err.message)//链式调用,500是错误的状态码
})
app.listen(3000)
console.log('网站服务器启动成功')
8、构建模块化路由
分类对路由进行管理,如可以进行二级路由的管理
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//创建路由对象
const home=express.Router()
//将路由和请求路径匹配
app.use('/home',home)
//在home路由下继续创建二级路由
home.get('/index',(req,res)=>{
//home/index访问路径
res.send('这里是二级路由展示页面')
})
app.listen(3000)
console.log('网站服务器启动成功')
9、导出模块化路由
模块一home.js
//引入express框架
const express=require('express')
//创建路由对象
const home=express.Router()
//将路由和请求路径匹配
home.get('/index',(req,res)=>{
//home/index访问路径
res.send('这里是home二级路由展示页面')
})
//导出路由
module.exports = home
模块二admin.js
//引入express框架
const express=require('express')
//创建路由对象
const admin=express.Router()
//在home路由下继续创建二级路由
admin.get('/index',(req,res)=>{
//home/index访问路径
res.send('这里是admin二级路由展示页面')
})
//导出路由
module.exports = admin
入口文件app.js
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//导入自定义二级路由
const home = require('./home')
const admin = require('./admin')
app.use('/home',home)
app.use('/admin',admin)
app.listen(3000)
console.log('网站服务器启动成功')
10、get参数的获取
Express框架中使用req.query即可获取get参数,框架内部会将get参数转换为对象并返回
//接受地址栏中问号后面的参数
// 例如: http://localhost:3000/?name=zhangsan&age=30
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
app.get('/index',(req,res)=>{
res.send(req.query)
})
app.listen(3000)
console.log('网站服务器启动成功')
11、post参数的获取
接受post参数需要第三方包body-parser
- 安装npm install body-parser
- 引入模块
实例文件
提交表单文件post.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:3000/add" method="post">
<label>姓名:</label>
<input type="text" name="name">
<label>年龄:</label>
<input type="text" name="age">
<input type="submit" value="提交">
</form>
</body>
</html>
入口文件app.js
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//引入bodyparser模块
const bodyparser=require('body-parser')
//对bodyparser模块进行设置
//extened:false 方法内部使用querystring模块处理请求参数的格式
//extended:true 方法内部使用第三方模块qs处理请求参数的格式
app.use(bodyparser.urlencoded({extened:false}))//将解析出来的对象存入body中,extend是扩展方法
//接受请求
app.post('/add',(req,res)=>{
//接受请求参数
res.send(req.body)//通过body属性获得post传来的参数对象
})
app.listen(3000)
console.log('网站服务器启动成功')
12、use方法再探
在use方法里面传递了一个参数,可是我也还是没有太理解这样做有什么意义,当然,拦截路由后,可以自定义函数,并根据传入的参数做相应的响应。
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
//引入bodyparser模块
const bodyparser=require('body-parser')
app.use(fn({a:1}))//拦截所有路由并调用自定义函数
function fn(obj){
return function(req,res,next){
if (obj.a==2){
console.log(req.url)
}else{
console.log(req.method)
}
next()
}
}
app.get('/',(req,res)=>{
res.send('ok')
})
app.listen(3000)
console.log('网站服务器启动成功')
13、Express路由参数
实际就是把参数写在路由里
使用一个占位符:id进行参数指定
如
app.get(‘/find/:id’,(req,res)=>{
console.log(req.param)//{id:123}
})
localhost:3000/find/123,其中123就是参数,123与:id是对应关系
案例
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
app.get('/find/:id',(req,res)=>{
console.log(req.params)
res.send('had print')
})
app.listen(3000)
console.log('网站服务器启动成功')
输入地址:http://localhost:3000/find/zhangsan
结果,服务器端打印{id:‘zhangsan’}
如果想传递多个参数可以继续在地址后面加/:属性如:’/find/:id/:name/:age’
//引入express框架
const express=require('express')
//直接通过express创建网站服务器
const app=express()
app.get('/find/:name/:age/:address',(req,res)=>{
console.log(req.params)
res.send('had print')
})
app.listen(3000)
console.log('网站服务器启动成功')
输入地址:http://localhost:3000/find/zhangsan/25/beijing
结果,服务器端打印{name:‘zhangsan’,age:25,address:beijing}
14、静态资源的处理
通过Express内置的express.static可以方便地托管静态文件,例如img、CSS、JavaScript 文件等。
app.use(express.static('public'))
现在,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
实际案例
//引入express框架
const express=require('express')
//引入path模块
const path=require('path')
//直接通过express创建网站服务器
const app=express()
//实现静态资源访问功能
app.use(express.static(path.join(__dirname,'public')))
app.listen(3000)
console.log('网站服务器启动成功')
地址栏里输入:http://localhost:3000/hello.html
可以直接访问/public下的hello.html文件
15、express-art-template模板引擎
- 为了使art-template模板引擎能够更好的和Express框架配合,模板引擎官方在原art-template模板引擎的基础上封装了express-art-template
- 使用npm install art-template express-art-template命令进行安装。(多个模块直接用空格隔开就可以了)
两个模板引擎文件index.art和list.art
index.art
{{msg}}
list.art
{{msg}}
主入口文件app.js
//引入express框架
const express=require('express')
//引入path模块
const path=require('path')
//直接通过express创建网站服务器
const app=express()
//1、告诉express框架使用什么模板引擎渲染什么后缀的模板文件
//1.1模板后缀(art)
//1.2使用模板引擎
//当渲染后缀为art的模板时,使用express-art-template
app.engine('art',require('express-art-template'))
//2设置模板存放目录,告诉express框架模板存放的位置是什么
app.set('views',path.join(__dirname,'views'))//views是模板的存放文件夹,第一个views是固定(且默认)的,第二个views是自定义的
//3、告诉express框架模板的默认后缀是什么
//渲染模板时不写后缀,默认拼接art后缀
app.set('view engine','art')
//index路由
app.get('/index',(req,res)=>{
//渲染模板
//1、拼接了模板路径
//2、拼接了模板后缀
//3、哪一个模板和哪一个数据进行了拼接
//4、将拼接结果响应给客户端
res.render('index',{
msg:'渲染模板的信息'
})
})
//list路由
app.get('/list',(req,res)=>{
res.render('list',{
msg:'list message'
})
})
app.listen(3000)
console.log('网站服务器启动成功')
16、app.loacls对象
将变量设置到app.locals对象下面,这个数据在所有的模板中都可以获取到
范例:
app.locals.users=[
{
name:'zhangsan',
age:23
},
{
name: 'lisi',
age:32
}
]
两个模板引擎文件index.art和list.art
index.art
{{msg}}
<ul>
{{each users}}
<li>
{{$value.name}}
{{$value.age}}
</li>
{{/each}}
</ul>
list.art
{{msg}}
<ul>
{{each users}}
<li>
{{$value.name}}
{{$value.age}}
</li>
{{/each}}
</ul>
主入口文件app.js
//引入express框架
const express=require('express')
//引入path模块
const path=require('path')
//直接通过express创建网站服务器
const app=express()
//1、告诉express框架使用什么模板引擎渲染什么后缀的模板文件
//1.1模板后缀(art)
//1.2使用模板引擎
//当渲染后缀为art的模板时,使用express-art-template
app.engine('art',require('express-art-template'))
//2设置模板存放目录,告诉express框架模板存放的位置是什么
app.set('views',path.join(__dirname,'views'))//views是模板的存放文件夹,第一个views是固定的,第二个views是自定义的
//3、告诉express框架模板的默认后缀是什么
//渲染模板时不写后缀,默认拼接art后缀
app.set('view engine','art')
//创建一个locals对象
app.locals.users=[
{
name:'zhangsan',
age:23
},
{
name: 'lisi',
age:32
}
]
//index路由
app.get('/index',(req,res)=>{
//渲染模板
//1、拼接了模板路径
//2、拼接了模板后缀
//3、哪一个模板和哪一个数据进行了拼接
//4、将拼接结果响应给客户端
res.render('index',{
msg:'渲染模板的信息'
})
})
//list路由
app.get('/list',(req,res)=>{
res.render('list',{
msg:'list message'
})
})
app.listen(3000)
console.log('网站服务器启动成功')
结果:
http://localhost:3000/list和http://localhost:3000/index两个路由都可以拿到app.js里面的对象数组
17、使用单独的路由页面
路由页面
router.js
//导入两个主要模块
let app=require('./expressapp')
let fs=require('fs')
module.exports=function(app){//把路由作为一个模块(包含参数的一个函数)导出方便在入口页面把app传入
app.get('/',(req,res)=>{
let students=fs.readFile('db.json','utf8',(err,data)=>{
if (err){
console.log(err)
}
let students=JSON.parse(data).students
res.render('index.html',{
fruits:[
'苹果',
'香蕉',
'桔子'
],
students: students
})
});
})
}
主入口文件
expressapp.js
const express=require('express')
const fs=require('fs')
const router=require('./router')
const app=express()
app.use('/public',express.static('./public'))
app.use('/node_modules',express.static('./node_modules'))
//1、配置模板引擎
app.engine('html',require('express-art-template'))
//在入口页面直接使用导入的路由模块,把app作为实参传入
router(app)
app.listen(3000)
console.log('the server is started at port 3000')
module.exports=app
18、使用express和router的标准方式封装路由代码
路由文件
router.js
//导入两个主要模块
let app=require('./expressapp')
let fs=require('fs')
//另外一种思路包装路由
let express=require('express')
//创建一个路由容器
let router=express.Router()
//把路由都挂载到路由容器中
router.get('/',(req,res)=>{
let students=fs.readFile('db.json','utf8',(err,data)=>{
if (err){
console.log(err)
}
let students=JSON.parse(data).students
res.render('index.html',{
fruits:[
'苹果',
'香蕉',
'桔子'
],
students: students
})
});
})
//把router路由容器导出
module.exports=router
入口文件
expressapp.js
const express=require('express')
const fs=require('fs')
const router=require('./router')
const app=express()
app.use('/public',express.static('./public'))
app.use('/node_modules',express.static('./node_modules'))
//1、配置模板引擎
app.engine('html',require('express-art-template'))
//把路由容器挂载到路由容器上
app.use(router)
app.listen(3000)
console.log('the server is started at port 3000')
module.exports=app
19、callback函数获取异步处理结果
当代码中含有异步数据的时候,可以通过callback的形式获取这个异步数据
代码定义:
function add(x,y,callback){
console.log('======')
setTimeout(function(){
var ret=x+y
callback(ret)
},1000)
}
//这里function的参数的结果就是异步调用的结果
add(4,6,function(ret)){
//return ret
console.log(ret)
}