一、Express
1.1 简介
- 官网:Express
- 概念:基于Node.js平台,快速、开放、极简的Web开发框架
- 作用:
1.2 基本使用
# 包初始化
npm init
# 安装
npm install express
npm i nodemon -g
# 启动
nodemon index.js # 或 node --watch index.js
// 1.get,post请求
app.get('请求url', (req, res) => {})
app.post('请求url', (req, res) => {})
// 2.把内容响应给客户端
res.send({ name: '张三', age: 18 })
// 3.获取url中携带的查询参数(/user?name=zs&age=18)
req.query // { name:zs, age: 18 }
// 4.获取url中的动态参数(/user/1)
app.get('/user/:id', (req, res) => {
console.log(req.params) // { id: '1' }
res.send({})
})
1.3 托管静态资源
// 1.托管静态资源目录
app.use(express.static('public'))
// 设置后直接访问public目录下的所有文件
// http://localhost:3000/image/bg.jpg
// http://localhost:3000/css/index.css
// 2.挂载路径前缀
app.use('/public', express.static('public'))
// http://localhost:3000/public/image/bg.jpg
// http://localhost:3000/public/css/index.css
// 3.托管多个
app.use(express.static('public'))
app.use(express.static('file'))
- 注意事项
- 存放静态文件的目录名不会出现在URL中
- 托管多个时会根据目录的添加顺序查找所需要的文件
二、Express 路由
2.1 路由概念
- 定义:客户端请求与服务端处理函数之间的
映射关系
- 格式:app.METHOD(PATH, HANDLER)
- 匹配规则:
- 按照定义的先后顺序匹配
- 请求类型和请求url同时匹配成功,才会调用对应处理函数
2.2 路由使用
// app上挂载路由
app.get('/', (req, res) => { res.send('hello world') })
app.post('/', (req, res) => { res.send('post') })
// 1.新建userRouter.js文件
const express = require('express');
const router = express.Router();
router.get('/user', (req, res) => {
res.send('get请求')
})
router.post('/user', (req, res) => {
res.send('post请求')
})
module.exports = router;
// 2.index.js 注册路由模块
const userRouter = require('./userRouter');
// 3.路由模块添加前缀(可选)访问 http://127.0.0.1:8000/api/user
app.use('/api', userRouter);
三、Express 中间件
3.1 中间件概念
- 定义:业务流程的
中间处理环节
- 格式:const mw = function(req, res, next){ next() }
- 注意:
- 中间件函数必须包含next参数,路由处理函数只有req,res
- next函数作用是把流转关系转交给下一个中间件或路由
3.2 中间件初体验
const mw = function(req, res, next){
console.log('这是一个中间件函数');
next();
}
app.use(mw);
// 简写
app.use((req, res, next) => {
console.log('调用第一个全局中间件');
next()
})
app.use((req, res, next) => {
console.log('调用第二个全局中间件');
next()
})
- 局部生效的中间件(不使用app.use定义的中间件,且只在当前路由生效)
const mw = function(req, res,next) {
console.log('这是中间件函数');
next();
}
const mw1 = function(req, res,next) {
console.log('这是中间件函数2');
next();
}
router.get('/user', mw, mw1, (req, res) => {
res.send('get请求')
})
// 等同于上述
// router.get('/user', [mw, mw1], (req, res) => {
// res.send('get请求')
// })
- 注意事项
- 多个全局中间件。请求到达服务器后会按照中间件定义的先后顺序执行
- 一定要在路由之前注册中间件
- 客户端发过来的请求,可以连续调用多个中间件进行处理
- 执行完中间件的业务代码后,需要调用next函数(不要在next后再写额外代码)
- 多个中间件之间共享同一份req和res。
3.3 中间件分类
- 应用级别的中间件
app.use((req, res, next) => { next() })
app.get('/', mw, (req, res) => {})
- 路由级别的中间件
const mw = function(req, res,next) {
console.log('这是中间件函数');
next();
}
router.get('/user', mw, (req, res) => {
res.send('get请求')
})
- 错误级别的中间件(必须注册在所有路由之后)
app.get('/page', (req, res) => {
throw new Error('服务器发生错误了');
res.send('page')
})
<!-- 四个形参 -->
app.use((err, req, res, next)=> {
console.log('发生错误了:' + err.message)
res.send('Error' + err.message)
})
- Express内置的中间件
- express.static:快速托管静态资源的内置中间件
- express.json:解析JSON格式的请求体数据
- express.urlencoded:解析x-www-form-urlencoded格式的请求体数据
- 请求体格式介绍
- JSON
- x-www-form-urlencoded
- 提交传统HTML表单
- 接口规范要求表单格式
- 需要兼容IE等老旧浏览器
- multipart/form-data
// postman -> body -> json
app.use(express.json())
app.post('/user', (req, res) => {
console.log(req.body); // 如果不配置express.json,获取undefined
res.send('发送成功')
})
// postman -> body -> x-www-form-urlencoded
app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
console.log(req.body); // 如果不配置urlencoded,获取undefined
res.send('发送成功')
})
- 第三方的中间件
- 如:body-parser中间件(现在使用express.urlencoded中间件)
- 安装:npm install body-parser
- 导入:require导入
- 注册:app.use
3.4 自定义中间件
- 手写express.urlencoded(body-parse)
const qs = require('querystring')
function bodyParse(req, res, next){
let rawData = '';
req.on('data', chunk => {
rawData += chunk
})
req.on('end', () => {
// 把字符串转换成对象格式: name=zs&age=18 -> { name: 'zs', age: '18' }
const body = qs.parse(rawData)
req.body = body
next();
})
}
module.exports = bodyParse;
const bodyParse = require('./body-parse')
app.use(bodyParse)
app.post('/user', (req, res) => {
console.log(req.body)
res.send('发送成功')
})
四、Express 写接口
4.1 接口编写
// 1.导入express
const express = require('express');
// 2.创建web服务器
const app = express();
const cors = require('cors')
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
// 正常情况下需要路由模块化
app.post('/postApi', (req, res) => {
res.send({
status: 0,
msg: 'post请求成功',
data: req.body
})
})
// 3.调用app.listen,启动服务器
app.listen(3000, () => {
console.log('runing at http://127.0.0.1')
}).on('error', err => {
console.log(err)
})
4.2 express解决跨域
- cors
- 定义:由一系列http响应头组成,这些响应头决定浏览器是否阻止JS代码跨域获取资源
- 相关响应头:
- Access-Control-Allow-Origin:允许访问该资源的外域URL
- Access-Control-Allow-Headers:cors仅支持客户端发送的9个响应头(否则需声明)
- Access-Control-Allow-Methods:cors仅支持get,post,head请求(否则需声明)
- 请求分类:
- 步骤
- 安装:npm install cors
- 导入:const cors = require(‘cors’)
- 注册:app.use(cors())
- 使用示例
// 使用
const cors = require('cors')
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.post('/postApi', (req, res) => {
res.send({
status: 0,
msg: 'post请求成功',
data: req.body
})
})
// 测试
$('#btnPost').on('click', () => {
$.ajax({
type: 'post',
url: 'http://127.0.0.1:3000/postApi',
data: { name: 'zs', age: 18 },
success: (res) => {
console.log(res)
}
})
})
- JSONP:略
4.3 跨域介绍
- 定义:由于浏览器同源策略:限制了不同源之间的资源访问
- 解决本质:
- 让请求’变同源’(代理方案)
- 让服务器’允许’(cors方案)
- 触发条件:
- 协议不同:
http://example.com -> https://example.com
- 域名不同:
https://example.com -> https://api.example.com
- 端口不同:
http://localhost:3000 -> http://localhost:5000
- 常见场景:
- AJAX请求:浏览器发送的跨域 HTTP 请求
- 字体,图片:网页加载时需要跨域请求字体、图片等静态资源
- WebSocket:客户端和服务器不在同一个源上
- 解决方案:
- cors:服务器添加响应头允许域名访问(如:Access-Control-Allow-Origin: ‘*’)
- jsonp:
<script>可以跨域加载js文件
- 1.前端定义回调函数:function handleData(data) { // 处理数据 }
- 2.动态创建
<script> 标签
const script = document.createElement('script');
script.src = 'http://api.com/data?callback=handleData'; // 挂载回调函数名
document.body.appendChild(script);
handleData({ name: "小明", age: 25 });
- 开发环境正向代理:
- 过程:/api/data ->
http://api.example.com/data
server: {
proxy: {
'/api': {
target: 'http://api.example.com',
changeOrigin: true, // 修改请求头中的Host
rewrite: path => path.replace(/^\/api/, '') // 去掉/api前缀
}
}
}
- Nginx反向代理:
- 过程:
https://your-domain.com/api/data -> 通过nginx -> http://api.example.com/data
# API代理规则
location /api/ {
proxy_pass http://api.example.com/; # 末尾斜杠很重要!
proxy_set_header Host $host;
}
- WebSockt:建立持久化跨域通道