node.js-express中间件/跨域问题
中间件
当一个请求到达express的服务器之后,可以连续第爱用多个中间件,从而对此次请求进行预处理 本质上是一个function处理 函数 注意:中间件函数的形参列表中,必须包含next,二路由只有req和res。 next函数是实现多个中间件连续调用的关键,他表示把流转关系转交给下一个中间件和路由。 全局中间件const express = require('express'); const app = express(); //定义一个简单的中间件函数 const mw = function (req, res, next) { console.log('这是一个简单的中间件函数'); //把流转关系,转交给下一个中间件或路由 next(); } //将mw注册为一个全局生效的中间件 app.use(mw); app.get('/', (req, res) => { console.log('调用了/'); res.send('home page') }); app.get('/user', (req, res) => { console.log('调用了/user'); res.send('user page') }) app.listen(80, () => { console.log('http://127.0.0.1'); })
postman测试
终端测试结果 http://127.0.0.1 这是一个简单的中间件函数 调用了/ 这是一个简单的中间件函数 调用了/user 中间件简化形式 app.use((req, res, next) => { console.log('这是一个中间件'); next(); })中间件的作用
多个中间件之间,共享同一份req和res。我们可以在上游的中间件中,统一为req和res对象添加自定义的属性和方法,恭下游的中间件和路由使用const express = require('express'); const app = express(); app.use((req, res, next) => { //获取请求到达服务器的时间 const time = Date.now(); req.startTime = time; console.log('这是一个中间件'); next(); }) app.get('/', (req, res) => { console.log('调用了/'); res.send('home page' + req.startTime) }); app.get('/user', (req, res) => { console.log('调用了/user'); res.send('user page' + req.startTime) }) app.listen(80, () => { console.log('http://127.0.0.1'); })
定义多个全局中间件
连续多次调用中间件const express = require('express'); const { expr } = require('jquery'); const app = express(); app.use((req, res, next) => { console.log('调用第一个中间件'); next(); }) app.use((req, res, next) => { console.log('调用第二个中间件'); next(); }) app.get('/', (req, res) => { res.send('home page') }) app.listen(80, () => { console.log('http://127.0.0.1'); })postman测试 发送get请求 终端结果如下 [nodemon] starting `node 定义多个中间件.js` http://127.0.0.1 调用第一个中间件 调用第二个中间件
局部生效的中间件
const express = require('express'); const app = express(); const mw1 = (req, res, next) => { console.log('调用了局部生效的中间件'); } app.get('/', mw1, (req, res) => { res.send('home page'); }); app.get('/user', (req, res) => { res.send('user page'); }); app.listen(80, () => { console.log('http://127.0.0.1'); })postman测试http://127.0.0.1/终端显示调用局部中间件 postman测试http://127.0.0.1/user,终端不显示调用局部中间件
定义多个局部中间件
const express = require('express'); const app = express(); const mw1 = (req, res, next) => { console.log('调用了局部生效的中间件1'); next() } const mw2 = (req, res, next) => { console.log('调用了局部生效的中间件2'); next() } app.get('/', mw1, mw2, (req, res) => { res.send('home page'); }); app.get('/user', [mw1, mw2], (req, res) => { res.send('user page'); }); app.listen(80, () => { console.log('http://127.0.0.1'); })postman测试http://127.0.0.1/ postman测试http://127.0.0.1/user [nodemon] starting `node 1局部中间件.js` http://127.0.0.1 调用了局部生效的中间件1 调用了局部生效的中间件2 调用了局部生效的中间件1 调用了局部生效的中间件2
中间件的分类
应用级别的中间件
绑定到app实例上的中间件,例如app.use(),app.get()等路由级别的中间件
绑定到router实例上的中间件,例如router.use(),router.get()等错误级别的中间件
用来捕获整个项目发送的异常错误,从而防止项目异常崩溃的问题。const express = require('express'); const app = express(); //定义路由 app.get('/', (req, res) => { //人为制造错误 throw new Error('服务器内部发生了错误!'); res.send('home page'); }) //定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃 app.use((err, req, res, next) => { console.log('发生了错误' + err.message); res.send('Error:' + err.message) }) app.listen(80, () => { console.log('express server running at http://127.0.0.1'); })
postman测试
终端显示结果 [nodemon] starting `node 2演示错误级别的中间件.js` express server running at http://127.0.0.1 发生了错误服务器内部发生了错误!内置中间件
const express = require('express'); const app = express(); //通过express.json()来解析客户端发送过来的JSON格式的数据 app.use(express.json()); //通过express.urlencoded()这个中间件,解析表单中的url-encoded格式的数据。 app.use(express.urlencoded({ extended: false })) app.post('/', (req, res) => { //在服务器,可以使用req.body这个属性,来接收客服端发送过来的额请求数据 //默认情况,如果不配置解析表单数据的中间件,则req.body默认等于undefined console.log(req.body); res.send('ok') }); app.post('/book', (req, res) => { //在服务器端,可以使用req.body这个属性来接收客服端发送过来的额请求数据 //默认情况,如果不配置解析表单数据的中间件,则req.body默认等于undefined console.log(req.body); res.send('ok') }); app.listen(80, () => { console.log('express server running at http://127.0.0.1'); })
postman中进行调试测试
[nodemon] starting `node 3演示内置中间件.js` express server running at http://127.0.0.1 { name: 'ss', age: 20 } [nodemon] starting `node 3演示内置中间件.js` express server running at http://127.0.0.1 [Object: null prototype] { bookname: '生命', author: '美国人', publisher: '天津' } 第三方的中间件const express = require('express'); const app = express(); //导入解析数据的中间件 body-parser const parser = require('body-parser'); //注册中间件 app.use(parser.urlencoded({ extended: false })) app.post('/user', (req, res) => { //没有配置任何解析表单数据的中间件,则req.body默认为undefined console.log(req.body); res.send('ok') }) app.listen(80, () => { console.log('express server running at http://127.0.0.1'); })注意:express内置的express.urlencoded中间件,就是基于body-parser这个第三方中间件进一步封装出来了。 自定义中间件
const express = require('express'); const app = express(); //解析表单中间件 //导入自己的中间件模块 const customBodyParser = require('./6custom-body-parser'); //将自定义的中间件函数,注册为全局可用的中间件 app.use(customBodyParser); app.post('/user', (req, res) => { res.send(req.body); }) app.listen(80, () => { console.log('express server running at http://127.0.0.1'); })
封装函数模块
const qs = require('querystring'); const bodyParser = (req, res, next) => { //定义一个字符串,专门用来存储客户端发送过来的请求体数据 let str = ""; req.on('data', (chunk) => { str += chunk; }); //监听req的end事件 req.on('end', () => { // console.log(str); //把字符串的请求体数据,解析成对象格式 const body = qs.parse(str); //将解析出来的数据对象挂载为req.body req.body = body; next(); }) } module.exports = bodyParser
使用Express写接口
//创建一个基本服务器 const express = require('express'); const app = express(); //配置解析表单数据的中间件 app.use(express.urlencoded({ extended: false })) //导入路由模块 const router = require('./8.路由模块'); //注册 app.use('/api', router); app.listen(80, () => { console.log('express server running at http://127.0.0.1'); })
路由模块
const express = require('express'); const router = express.Router(); //挂载对于的路由 router.get('/get', (req, res) => { //通过req.query获取客户端通过查询字符串,发送到服务器的数据 const query = req.query; //调用res.send方法,想客服端响应处理结果 res.send({ status: 0,//0成功,1失败 msg: 'GET 请求成功', data: query//需要响应给客户端的数据 }) }) //定义post接口 router.post('/post', (req, res) => { //通过req.body获取请求体中包含的url-encoded格式的数据 const body = req.body; //调用res.send,向客户端响应结果 res.send({ status: 0, msg: 'POST 请求成功', data: body }) }) module.exports = router;
postman测试结果
加入查询条件之后
接口跨域问题
解决端口跨域问题的方案主要有两种: CORS:推荐 JSON P:只支持GET请求 使用CORS中间件解决跨域问题//创建一个基本服务器 const express = require('express'); const app = express(); //配置解析表单数据的中间件 app.use(express.urlencoded({ extended: false })); //在路由之前,配置中间件 const cors = require('cors'); app.use(cors()); //导入路由模块 const router = require('./8.路由模块'); //注册 app.use('/api', router); app.listen(80, () => { console.log('express server running at http://172.20.10.3'); })
加入cors中间件之后,可以解决跨域问题
在浏览器中打开,进行get和post请求成功
预检请求
在浏览器与服务器正式通信之前,浏览器会先发送OPTION请求进行预检,以获知服务器是否允许该实际请求,所以这一次的OPTION请求称为“预检请求”,服务器成功相应预检请求后,才会发送正真的请求,并且携带真实数据。 简单请求和预检请求的区别 简单请求的特点:客户端与服务器之间只会发生一次请求 预检请求的特点:客户端与服务器之间会发生两次请求,OPTION预检请求成功之后,才会发起真正的请求。JSONP接口
//必须在cors之前创建jsonp接口 app.get('/api/jsonp', (req, res) => { }) //在路由之前,配置中间件 const cors = require('cors'); app.use(cors());
在网页中使用jquery发起jsonp请求
app.get('/api/jsonp', (req, res) => { //得到函数的名称 const funcname = req.query.callback; //定义要发送到客服端的数据对象 const data = { name: "ss", age: 20 }; //拼接以函数的调用 const scriptstr = funcname + '(' + JSON.stringify(data) + ')'; //把拼接的字符串,响应给客户端 res.send(scriptstr); })
//3.为JSONP绑定事件 $("#btnJSONP").on('click', function () { $.ajax({ type: 'GET', url: 'http://172.20.10.3/api/jsonp', dataType: 'jsonp', success: function (res) { console.log(res); } }) })