【Node中间层实践(四)】----express中间层的逻辑处理

前言:
    本文是学习Node中间层实践的相关笔记,推荐大家可以去阅读Keyon Y大佬的原文Node中间层实践 。开启本文前,也可以先去阅读以下,Node中间层实践(一)——基于NodeJS的全栈式开发,对了解本文内容有所帮助。(PS:继续还债的node小白╮(╯▽╰)╭)
    express的逻辑处理分为两部分:转发后端接口中间件

一、转发后端接口

后端提供的接口分为两类:

  • 渲染页面使用的数据接口;
  • 前端部分发送ajax请求的请求接口
数据接口

就是url的链接所能访问的页面。
先使用router.get()注册路由,路由内先通过axios访问后端获取页面数据response,通过res.render()返回一个自定义对象,将response作为对象的属性传递到pug模板页面中,在pug页面中便可以随意使用response 。
config/routes/default.js文件:

// 首页
router.get('/', addUser, (req, res, next) => {
	axios.all([
		axios.get('/Api/Carous'),
		axios.get('/Api/Cars/Top10', {params: {page: req.query.page || 1}}),
	])
		.then(axios.spread(function (res1, res2){
			config.throwError(next, res1, res2);
			var page = req.query.page || 1;
			res.render('Default/index', {
				title: config.title('首页'),
				keywords: config.keywords,
				description: config.description,
				menuNav: 0,
				carList: res1.data.Data,
				top10: res2.data.Data.slice((page-1)*3,page*3)
			});
		})).catch(e => {
		config.renderError(req, res, e);
	})
});

请求接口

是前端发送ajax请求的接口。
与数据接口的区别就是,中间层只做两件事:

  • 使用axios,转发前端的请求给后端;
  • 使用res.send(),转发给后端的响应给前端。
// get请求
router.get('/User/Role', (req, res, next) => {
    axios.get(config.origin + '/Api/User/Role', config.axiosHeaders(req, {
        params: {role: req.query.role}
    }))
        .then(res1 => {
            res.send(res1.data);
        }).catch(e => {
            config.sendError(res, e);
    })
})

// post请求
router.post('/User/SendRequest', (req, res, next) => {
	axios.post(config.origin + '/Api/User/SendRequest', {
		userID: req.body.userID || null,
		requestID: JSON.parse(req.body.requestID) || null
	}, config.axiosHeaders(req))
		.then((res2) => {
			res.send(res2.data);
		}).catch((e) => {
		config.sendError(res, e);
	})
});

post 请求无法直接读取请求体,需要安装body-parser依赖

npm i –save body-parser

并在app.js挂载这个依赖

app.use(bodyParser.urlencoded({extended: false}));

获取前端请求中的参数

分为三种情况:

  • 获取带有参数的路由中的参数
router.get('/Cars/:id', (req, res, next) => {
    axios.get(config.origin + '/Api/Cars', {
        params: {role: req.params.id}  // req.params.id获取url中的参数
    }))
    ...
})

  • 获取get请求的参数
// 例如这样的请求 /Cars/Search?q=tobi
router.get('/Cars/Search', (req, res, next) => {
    axios.get(config.origin + '/Api/Cars/Search', {
        params: {car: req.query.q}  // req.query.q获取参数
    }))
    ...
})

  • 获取post请求的参数
router.post('/Cars/Search', (req, res, next) => {
    axios.post(config.origin + '/Api/Cars/Search', {
        car: req.body.name  // req.body.name获取请求体中的参数
    }))
    ...
})

  • post请求转发数组对象
    express post请求,获取客户端请求中是数组时,若是直接post一个数组,则express无法获取数组,需要在客户端对数组进行序列化,在中间层反序列化:
// 客户端
$.ajax({
	url: '/Api/Mas/SendInfo',
	type: 'POST',
	data: {userID: userId, requestID: JSON.stringify(selected)},  // 序列化
	success: function (res) {
		log(res);
		if(res.Status == 0){
			$thisModal.hide();
			$('#modal_cds').show();
		}
	}
})
		
// 中间层
axios.post(config.origin + '/Api/Mas/SendInfo', {
	userID: req.body.userID || null,
	requestID: JSON.parse(req.body.requestID) || null    // 反序列化
}, config.axiosHeaders(req))

二、中间件

中间层不要传递有可能不存在的属性

千万不要在中间层传递有可能不存在的property!
千万不要在中间层传递有可能不存在的property!
千万不要在中间层传递有可能不存在的property!
重要的事情说三遍,不然出现下面的bug都不知道在哪里找。

(node:20352) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property ‘data’ of undefined

中间件

Express是一个自身功能极简,完全是由路由和中间件构成的一个的web开发框架:从本质上来说,一个Express应用就是在调用各种中间件。
中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)),响应对象(response object (res)),和web应用中处于请求–响应循环流程中的中间件,一般被命名为next的变量。
中间件的功能代码:

  • 执行任何代码;
  • 修改请求和响应对象;
  • 终结请求–响应循环;
  • 调用堆栈中的下一个中间件。
    如果当前中间件没有终结请求–响应循环,则必须调用next()方法将控制权交给下一个中间件,否则请求就会挂起。
    Express应用可使用如下几种中间件:
  • 应用级中间件
  • 路由级中间件
  • 错误处理中间件
  • 内置中间件
  • 第三方中间件
    使用可选择挂载路径,可在应用级别或路由级别装载中间件。另外,你还可以同时装载一系列中间件函数,从而在一个挂载点上创建一个子中间件栈。
使用路由级中间件处理request对象
var addUser = require('../../middleware/addUser');
    ...
router.get('/Car/List', addUser, (req, res, next) => {
    ...
})

addUser即中间件,是一个方法,接受路由中的req,res,next三个参数,必须以**next();**结束代码,这样讲函数指针从addUser,交给(req,res,next)=> {…}。
使用中间件,可以获取请求头中的cookies,以验证用户登录信息
获取cookie的内容,需要安装cookie-parser依赖

npm i –save cookie-parser

在app.js中,挂载这个依赖就可以使用了。

app.use(cookieParser());
错误处理中间件–Error Handle

注意Error Handle的挂载的顺序,一般挂载到app.use()下,且放在最后。

var express = require('express');
var cookieParser = require('cookie-parser');
var path = require('path');
var localOptions = require('./build/localOptions');
var config = require('./config/config');
var bodyParser = require('body-parser');
var auth = require('./middleware/auth');
var log4js = require('./config/log4js');

process.env.NODE_ENV = process.env.NODE_ENV ? process.env.NODE_ENV : 'production';
var isDev = process.env.NODE_ENV === 'dev';
var app = express();
var port = config.port;


app.set('view engine', 'pug');
// 设置模板文件路径
if (isDev){
	app.set('views', path.resolve(__dirname, 'src/Views'));
}else {
	app.set('views', path.resolve(__dirname, 'dist/Views'));
}

// app.locals定义的键值对能在模板中直接访问
app.locals.env = process.env.NODE_ENV || 'dev';
app.locals.reload = true;

app.use(cookieParser());
app.use(bodyParser.urlencoded({extended: false}));


if (isDev) {
    // app.locals.pretty = true;
	// 开发环境,静态文件使用热插拔
	var webpack = require('webpack');
	var webpackDevMiddleware = require('webpack-dev-middleware');
	var webpackHotMiddleware = require('webpack-hot-middleware');
	var webpackDevConfig = require('./build/webpack.dev.config.js');

	var compiler = webpack(webpackDevConfig);
	// 热插拔
	app.use(webpackDevMiddleware(compiler, {
		publicPath: webpackDevConfig.output.publicPath,
		noInfo: true,
		stats: 'errors-only'
	}))
	app.use(webpackHotMiddleware(compiler, {
		heartbeat: 1000,
		noInfo: true,
	}));

	// 不能热插拔的往下执行
	var reload = require('reload');
	var http = require('http');
	var server = http.createServer(app);
	// reload(server, app);
	reload(app);
	server.listen(port, () => {
		console.log('App【dev】 is now running on port ' + port + '!');
	});

	// 静态目录设置必须有,开发环境读取的vendor.js不是内存文件;
    // 静态目录设置必须放在reload后面,避免页面引入reload.js报错
    app.use(express.static(path.join(config.root, 'src')));
    app.use('/', require(path.join(config.configRoot,'/routes')));
	
}else {
	// 线上环境不需要监听,只需开启node服务即可
	// 设置node的静态文件目录
	app.use(express.static(path.join(config.root, 'dist')));
    app.use('/',require(path.join(config.configRoot,'/routes')));
    // app.listen(process.env.PORT, () => {
    app.listen(port, () => {
		console.log('App【production】 is now running on port ' + port + '!');
    })
}

// 捕捉 404错误 传给 error路由
app.use('*', auth, (req, res, next) => {
	let err = new Error('Not Found');
	err.status = 404;
	next(err);
});

// 捕获 error,跳转至error页面
app.use((err, req, res, next) => {
	const sc = err.status || 500;
	if(err.status == 405){
		res.redirect(config.cndesignOrigin + '/Home/GuideAuthentication');
		return;
	}
	res.status(sc);
	// 此处需写入日志
	log4js.error('\r\n Status: '+ sc + "\r\n Message: " + err.message + "\r\n Href: " + localOptions.baseUrl + req.originalUrl + "\r\n User-agent: " + req.headers['user-agent']);

	res.render('Error/404', {
		error: err,
		status: sc,
		message: err.message,
		userInfo: req.userInfo_ || { hasLogin: false }
	});
});

参考博客

Node中间层实践(五)——express-中间层的逻辑处理
https://juejin.cn/post/6844904191635226632

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值