Express
基于 Node.js 平台的 Web 开发框架
const express = require('express')
const app = express()
app.use('/', (req, res) => { //路由
res.send('hello') //响应信息
})
app.listen(8080, () => { //监听端口
console.log('正在监听8080...');
})
执行顺序
在使用app.use()配置路由时,类似正则匹配
如"/index"因为有"/“所以既会匹配”/index"也会匹配"/"的路由
这时候就基于路由排列顺序执行,且只会执行第一个匹配到的路由
中间件
app.use后的回调函数也称为中间件,后面可以接多个回调函数,也可以将其组合封装成数组
回调函数中有第三个参数next,类似迭代器,用于顺序执行中间件栈
中间件的功能:
1.执行任何代码。
2.修改请求和响应对象。
3.终结请求-响应循环。
4.调用堆栈中的下一个中间件。
中间件栈
app.use('/', (req, res, next) => { //多个中间件组成中间件栈
console.log('1');
next()
}, (req, res, next) => {
console.log('2');
next()
}, (req, res) => {
console.log('3');
})
--------------------或----------------------
const middlewares = [(req, res, next) => { //或用数组封装
console.log('1');
next()
}, (req, res, next) => {
console.log('2');
next()
}, (req, res, next) => {
res.send('3')
next()
}]
app.use('/', middlewares)
会将所有匹配到的路由的中间件组成一个中间件栈,只要有next()就能执行下一个中间件
app.use('/', (req, res, next) => {
console.log('1');
next()
}, (req, res, next) => {
console.log('2');
next()
}, (req, res, next) => {
res.send('3')
next()
})
app.use('/i', (req, res) => { //多个app.use()的中间件同样为栈
console.log('hello11');
})
//访问localhost:xxxx/i 输出结果1,2,hello11
res.send()并不会影响中间件栈的执行,只有当其中一个中间件没有next()时,才会停止后续中间件的执行
无论是全局还是局部中间件 先后顺序都是按照在代码中的代码书写顺序
写在next()后的代码同样会执行
next()本质上就是下一个中间件函数
app.use((req, res, next) => {
console.log('1 fired')
next()
console.log('1 end')
}, (req, res, next) => {
console.log('2 fired')
next()
console.log('2 end')
}, (req, res, next) => {
console.log('3 fired')
})
/* 输出结果为
1 fired
2 fired
3 fired
2 end
1 end
*/
当中间件不加路由时,所有请求都会调用一次
app.use('/i', (req, res) => {
console.log('hello11');
})
app.use(() => { //没有路由所以相当于都会匹配,每次请求均会执行
console.log(0);
})
express中间件
路由中间件
const express = require('express')
const route = express.Router()
route.get('/', (req, res) => {
res.send('我是11')
})
route.get('/index', (req, res) => {
res.json(req.query) //返回请求参数
})
route.post('/index', (req, res) => {
res.send(req.body) //返回请求体
})
route.put('/', (req, res) => {
res.send('我是22')
})
route.patch('/', (req, res) => {
res.send('我是33')
})
route.delete('/', (req, res) => {
res.send('我是44')
})
route.all('/', (req, res) => { //特殊请求方式,只要路由符合 所有的请求都会响应(简化代码)
res.send('我是all')
})
module.exports = {
route
}
此时与http的路由类似,只有路径相同且route方法也与请求方式匹配才会响应
route.get('/index', (req, res) => { //当请求路由为/index且为get请求才有响应(其他请求不作响应)
res.json(req.query)
})
路由通配符
router.use('/a/*/d',(req, res)=>{
console.log('测试通配符')
})
// 此时*号进行任意值任意长度的匹配
router.use('/k/:kfc',(req, res)=>{
console.log('获取params参数' , req.params)
})
// /k/后的值被变量kfc接收 并可以通过req.params访问
设置响应头
res.set('Content-Type','application/json; charset=utf-8')
或
res.header('Content-Type','application/json; charset=utf-8')
http-proxy-middleware中间件
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
cors中间件
const cors = require('cors')
app.use(cors()) // 解除跨域限制
jsonwebtoken中间件
在中间件中将token加密挂载到req中,后续业务逻辑就能通过req.sign进行调用
const jwt = require('jsonwebtoken')
app.use((req, res, next)=>{
req.sign = (res)=>{
return jwt.sign(res, '123456')
}
next()
})
鉴权
app.use((res, req, next) => {
const { url } = req
if(url === '/user/login') return next()
// authorization 惯例携带token的参数
const tk = req.header.authorization
try{
const decode = jwt.verify('tk', '123456')
req.decode = decode
next()
} catch(e){
res.status(401).end()
}
})
// 实际中一般token格式为 authorzation:'Bear tk' tk为加密的token
express内置中间件
用于托管静态文件,如果使用多个静态目录则多次调用此中间件
app.use(express.static('public')) //public静态文件夹名
express设置响应头跨域
注意需要在注册路由之前
app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', '*');
res.header('Content-Type', 'application/json;charset=utf-8');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
next();
});
controller抽离
当中间件的让业务逻辑过多时,需要把逻辑进行模块化
即将controller抽离出来成为一个单独的模块 在路由中间件引用
cookie与session
服务端响应可以设置cookie与cookie-session
session本质上就是cookie 是加密后的cookie
响应设置cookie
app.get((req, res)=>{
const _d = new Date()
_d.setSeconds(_d.getSeconds() + 5)
res.cookie('abc', 123, {
expires: _d, // 约定cookie到期时间
path: '/', // 约定cookie的可访问路径
httpOnly: true
// httpOnly 是否值允许请求的时候对cookie进行操作 不允许js去操作这个cookie
})
res.send('ok')
})
通过response响应设置cookie 以及路径、过期时间、是否能被js修改
响应设置的cookie在请求时也会携带 但需要中间件cookie-parse
const cookieParser = require('cookie-parser')
app.use(cookieParser())
app.use((req,res)=>{
console.log(req.cookies)
res.end()
})
响应设置session
需要先引入中间件cookie-session
const cookieSession = require('cookie-session')
app.use(cookieSession({
name: 'session', // 保存到客户端的cookie的name
secret: '123456', // 加密密钥
maxAge: 24*60*60*1000 // 过期时间
}))
app.use((req,res)=>{
req.session.view = 1 // 设置一个session的值
res.send('ok')
})
app.use((req,res)=>{
console.log('session', req.session) // 通过 req.session 也可以读取session
res.send('ok')
})
express脚手架
安装脚手架
npm install express-generator -g
创建项目
express demo
修改端口
在浏览器中使用 localhost:3000访问,默认的端口就是3000
app.js修改端口配置
process.env.PORT = 2000;
渲染
页面渲染(render)分为客户端渲染和服务器渲染
SSR(Server Side Render)
CSR(Client Side Render)
express的渲染模板
ejs
pug
jade
art-template
art-template模板
下载art-template以及express-art-template
yarn add art-template express-art-template -S
环境配置
//view engine setup
const path = require('path')
app.engine('art', require('express-art-template'))
app.set('view options', { //注意此处与官网不同
debug: process.env.NODE_ENV !== 'production',
escape: false //是个坑,转化HTML5代码
})
app.set('views', path.join(__dirname, './views'));
app.set('view engine', 'art')
同文件夹下创建view文件夹 在其中创建list.art用于处理数据
{
"ret": true,
"data": {{data}} //这里的{{data}}中的data为下方res.render()处理完的数据
}
然后在controller中的中间件(此处为list)返回数据
const list = () => {
let dataArray=[]
for(let i = 0; i < 100; i++){
dataArray.push('line' + i) //生成模拟数据
}
res.set('Content-Type','application/json; charset=utf-8')
res.render('list',{ //express本身没有render() 下载模板后才能使用
data: JSON.stringify(dataArray) //读取list.art下的data并赋值
})
}
export.list = list
以上均为后端!!!
前端渲染
const str = `
<ul>
{{each data}} //循环data
<li>{{$value}}</li> //值放入li中
{{/each}}
</ul>
`
let html = template.render(str,{data: result.data})
//这里的result为ajax请求后端返回的成功回调结果
//此处省略了外部的ajax
$('#list').html(html)