在本文中,您将会了解到关于详解vue+webpack+express中间件接口使用的新资讯,同时我们还将为您解释vue中间件的相关在本文中,我们将带你探索详解vue+webpack+express中间
在本文中,您将会了解到关于详解vue+webpack+express中间件接口使用的新资讯,同时我们还将为您解释vue 中间件的相关在本文中,我们将带你探索详解vue+webpack+express中间件接口使用的奥秘,分析vue 中间件的特点,并给出一些关于express.js中间件说明详解、express_webpack自动刷新、express中间件、express中间件加载机制示例详解的实用技巧。
本文目录一览:- 详解vue+webpack+express中间件接口使用(vue 中间件)
- express.js中间件说明详解
- express_webpack自动刷新
- express中间件
- express中间件加载机制示例详解
详解vue+webpack+express中间件接口使用(vue 中间件)
环境:vue 2.9.3; webpack
目的:接口的调用
跨域方式:
1、express中间的使用
2、Nginx代理
3、谷歌浏览器跨域设置
--------------------------------------------分割线---------------------------------------------
express中间件---不推荐
原理:本地代码请求->express中间件(处理,添加headers后转发)->服务器
express中间件 medical
本地代码文件目录 pacs
#########配置接口
在服务器中间件配置 medical/routes/home.js 没有需要新建home.js // 哪个文件使用就可以名字命名 这里就是home页面的接口
home.js
res.send('respond with a resource');
});
// 获取频道
router.post('/aa',res) {
http.post(ip + 'aaaa',req.body).then((data) => {
//console.log(JSON.stringify(data))
res.send(data);
})
});
// 主页 这就是要用到的接口
router.post('/main',res) {
http.post(IP+'/xhhms/rest/interfacesLoginController/getMenu',req.body,req.headers).then((data) => {
console.log(IP+'/xhhms/rest/interfacesLoginController/getMenu');
res.send(data);
})
});
上面的需要定义constant.js的ip
类似这样定义即可。
#########服务器中间的app.js里面引用
添加var home = require('./routes/home');
然后使用 app.use('/home',home);
######这样就ok了,就可以调用这个接口了
##### 因为上面用到了header里面的X-AUTH-TOKEN,需要修改中间件的配置文件,如果不用服务器中间件的调用的直接添加表头即可。
1、当前项目的传递方式
本地代码(pacs)----------->服务器中间件(web)------------>服务器
相当于多用了一层,中间层主要用来解决跨域等其他问题(却显得累赘)。
如上图,传递的参数存放在req.body里面,传递的header存放在req.headers里面,我们请求接口后就把req传递到
ykt-http-client里面,目录是在medical/node_modules/ykt-http-client/index.js里面 如果只是传递参数的话就没有问题,因为原来的脚本里面默认是吧req.body传递过去的,但是却没有req.headers保存的参数,所以需要修改下当前的index.js文件
这样的话,如果请求里面发现了headers参数就会传递到服务器里面,才能达到目的。
Nginx代理---不推荐
原理和中间大同小异,也是通过转发的方式。
谷歌浏览器跨域---推荐
参考网址:进行跨域后
直接通过vue-resource进行请求即可。
首先安装vue-resource
然后在src/main.js里面引入
###由于使用了浏览器跨域设置,这里面不需要设置什么。
###使用的话直接在各个位置使用即可
----由于刚刚接触vue,使用过程中也是绕了很多圈子,难受。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小编。
express.js中间件说明详解
express的新开发人员往往对路由处理程序和中间件之间的区别感到困惑。因此他们也对app.use(),app.all(),app.get(),app.post(),app.delete()和app.put()方法的区别感到困惑。
在本文中,我将解释中间件和路由处理程序之间的区别。以及如何正确使用app.use(),app.all(),app.get(),app.post(),app.delete()和app.put()方法。
路由处理
app.use(),app.all(),app.get(),app.post(),app.delete()和app.put()全部是用来定义路由的。这些方法都用于定义路由。路由用于处理HTTP请求。路由是路径和回调的组合,在请求的路径匹配时执行。回调被称为路由处理程序。
它们之间的区别是处理不同类型的HTTP请求。例如: app.get()方法仅仅处理get请求,而app.all()处理GET、POST等请求。
下面是一个例子,如何定义一个路由:
var app = require("express")(); app.get("/", function(req, res, next){ res.send("Hello World!!!!"); }); app.listen(8080);
每个路由处理程序都获得对当前正在提供的HTTP请求的请求和响应对象的引用。
可以为单个HTTP请求执行多个路由处理程序。这是一个例子:
var app = require("express")(); app.get("/", function(req, res, next){ res.write("Hello"); next(); }); app.get("/", function(req, res, next){ res.write(" World !!!"); res.end(); }); app.listen(8080);
这里第一个句柄写入一些响应,然后调用next()。 next()方法用于调用与路径路径匹配的下一个路由处理程序。
路由处理程序必须结束请求或调用下一个路由处理程序。
我们还可以将多个路由处理程序传递给app.all(),app.get(),app.post(),app.delete()和app.put()方法。
这是一个证明这一点的例子:
var app = require("express")(); app.get("/", function(req, res, next){ res.write("Hello"); next(); }, function(req, res, next){ res.write(" World !!!"); res.end(); }); app.listen(8080);
中间件
中间件是一个位于实际请求处理程序之上的回调。它采用与路由处理程序相同的参数。
要了解中间件,我们来看一个带有dashboard和profile页面的示例站点。要访问这些页面,用户必须登录。还会记录对这些页面的请求。
以下是这些页面的路由处理程序的代码:
var app = require("express")(); function checkLogin(){ return false; } function logRequest(){ console.log("New request"); } app.get("/dashboard", function(req, res, next){ logRequest(); if(checkLogin()){ res.send("This is the dashboard page"); } else{ res.send("You are not logged in!!!"); } }); app.get("/profile", function(req, res, next){ logRequest(); if(checkLogin()){ res.send("This is the dashboard page"); } else{ res.send("You are not logged in!!!"); } }); app.listen(8080);
这里的问题是有很多重复的代码,即我们不得不多次使用logRequest()和checkLogin()函数。这也使得更新代码变得困难。因此,为了解决这个问题,我们可以为这两条路径编写一条通用路径。
这是重写的代码:
var app = require("express")(); function checkLogin(){ return false; } function logRequest(){ console.log("New request"); } app.get("/*", function(req, res, next){ logRequest(); next(); }) app.get("/*", function(req, res, next){ if(checkLogin()){ next(); } else{ res("You are not logged in!!!"); } }) app.get("/dashboard", function(req, res, next){ res.send("This is the dashboard page"); }); app.get("/profile", function(req, res, next){ res.send("This is the dashboard page"); }); app.listen(8080);
这里的代码看起来更清晰,更易于维护和更新。这里将前两个定义的路由处理程序称为中间件,因为它们不处理请求,而是负责预处理请求。
Express为我们提供了app.use()方法,该方法专门用于定义中间件。 app.use()方法可能看起来与app.all()类似,但它们之间存在很多差异,这使得app.use()非常适合于声明中间件。让我们看看app.use()方法是如何工作的:
app.use() 和 app.all() 的不同:
CALLBACK
app.use()只需要一个回调,而app.all()可以进行多次回调。
PATH
app.use()只查看url是否以指定路径开头,app.all()匹配完整路径。
这里有一个例子来说明:
app.use( "/product" , mymiddleware); // will match /product // will match /product/cool // will match /product/foo app.all( "/product" , handler); // will match /product // won''t match /product/cool <-- important // won''t match /product/foo <-- important app.all( "/product/*" , handler); // won''t match /product <-- Important // will match /product/cool // will match /product/foo
NEXT()
中间件内的next()调用下一个中间件或路由处理程序,具体取决于接下来声明的那个。但是路由处理程序中的next()仅调用下一个路由处理程序。如果接下来有中间件,则跳过它。因此,必须在所有路由处理程序之前声明中间件。
这里有一个例子来说明:
var express = require(''express''); var app = express(); app.use(function frontControllerMiddlewareExecuted(req, res, next){ console.log(''(1) this frontControllerMiddlewareExecuted is executed''); next(); }); app.all(''*'', function(req, res, next){ console.log(''(2) route middleware for all method and path pattern "*", executed first and can do stuff before going next''); next(); }); app.all(''/hello'', function(req, res, next){ console.log(''(3) route middleware for all method and path pattern "/hello", executed second and can do stuff before going next''); next(); }); app.use(function frontControllerMiddlewareNotExecuted(req, res, next){ console.log(''(4) this frontControllerMiddlewareNotExecuted is not executed''); next(); }); app.get(''/hello'', function(req, res){ console.log(''(5) route middleware for method GET and path patter "/hello", executed last and I do my stuff sending response''); res.send(''Hello World''); }); app.listen(80);
现在我们看到了app.use()方法的唯一性以及它用于声明中间件的原因。
让我们重写我们的示例站点代码:
var app = require("express")(); function checkLogin(){ return false; } function logRequest(){ console.log("New request"); } app.use(function(req, res, next){ logRequest(); next(); }) app.use(function(req, res, next){ if(checkLogin()){ next(); } else{ res.send("You are not logged in!!!"); } }) app.get("/dashboard", function(req, res, next){ res.send("This is the dashboard page"); }); app.get("/profile", function(req, res, next){ res.send("This is the dashboard page"); }); app.listen(8080);
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
- 浅谈express 中间件机制及实现原理
- 深入理解nodejs中Express的中间件
- express的中间件bodyParser详解
- express的中间件basicAuth详解
express_webpack自动刷新
现在,webpack可以说是最流行的模块加载器(module bundler)。一方面,它为前端静态资源的组织和管理提供了相对较完善的解决方案,另一方面,它也很大程度上改变了前端开发的工作流程。在应用了webpack的开发流程中,想要继续“自动刷新”的爽快体验,就可能得额外做一些事情。
webpack与自动刷新
本文并不打算介绍webpack,如果你还不清楚它是什么,推荐阅读下面几篇入门文章:
-
Beginner’s guide to Webpack
-
Developing with Webpack
-
webpack-howto
webpack要求静态资源在被真正拿来访问之前,都要先完成一次编译,即运行完成一次webpack
命令。因此,自动刷新需要调整到适当的时间点。也就是说,修改了css等源码并保存后,应该先触发一次webpack编译,在编译完成后,再通知浏览器去刷新。
开发Express项目的问题
现在有这样的一个应用了webpack的Express项目,目录结构如下:
其中,client
内是前端的静态资源文件,比如css、图片以及浏览器内使用的javascript。server
内是后端的文件,比如express的routes、views以及其他用node执行的javascript。根目录的app.js
,就是启动express的入口文件了。
开发的时候我们会怎样做呢?
先启动Express服务器,然后在浏览器中打开某个页面,接下来再编辑源文件。那么,问题就来了,比如我编辑.scss
源文件,即使我只改了一小点,我也得在命令行里输入webpack
等它编译完,然后再切到浏览器里按一下F5,才能看到修改后的效果。
再比如,我修改了routes
里的.js
文件想看看结果,我需要到命令行里重启一次Express服务器,然后同样切到浏览器里按一下F5。
这可真是太费事了。
所以,我们要让开发过程愉快起来。
改进目标
我们希望的Express&Webpack项目的开发过程是:
-
如果修改的是
client
里的css文件(包括.scss
等),保存后,浏览器不会整页刷新,新的样式效果直接更新到页面内。 -
如果修改的是
client
里的javascript文件,保存后,浏览器会自动整页刷新,得到更新后的效果。 -
如果修改的是
server
里的文件,保存后,服务器将自动重启,浏览器会在服务器重启完毕后自动刷新。
经过多次尝试,我最终得到了一个实现了以上这些目标的项目配置。接下来,本文将说明这个配置是如何做出来的。
从webpack-dev-server开始
首先,webpack已经想到了开发流程中的自动刷新,这就是webpack-dev-server。它是一个静态资源服务器,只用于开发环境。
一般来说,对于纯前端的项目(全部由静态html文件组成),简单地在项目根目录运行webpack-dev-server,然后打开html,修改任意关联的源文件并保存,webpack编译就会运行,并在运行完成后通知浏览器刷新。
和直接在命令行里运行webpack
不同的是,webpack-dev-server会把编译后的静态文件全部保存在内存里,而不会写入到文件目录内。这样,少了那个每次都在变的webpack输出目录,会不会觉得更清爽呢?
如果在请求某个静态资源的时候,webpack编译还没有运行完毕,webpack-dev-server不会让这个请求失败,而是会一直阻塞它,直到webpack编译完毕。这个对应的效果是,如果你在不恰当的时候刷新了页面,不会看到错误,而是会在等待一段时间后重新看到正常的页面,就好像“网速很慢”。
webpack-dev-server的功能看上去就是我们需要的,但如何把它加入到包含后端服务器的Express项目里呢?
webpack-dev-middleware和webpack-hot-middleware
Express本质是一系列middleware的集合,因此,适合Express的webpack开发工具是webpack-dev-middleware和webpack-hot-middleware。
webpack-dev-middleware是一个处理静态资源的middleware。前面说的webpack-dev-server,实际上是一个小型Express服务器,它也是用webpack-dev-middleware来处理webpack编译后的输出。
webpack-hot-middleware是一个结合webpack-dev-middleware使用的middleware,它可以实现浏览器的无刷新更新(hot reload)。这也是webpack文档里常说的HMR(Hot Module Replacement)。
参考webpack-hot-middleware的文档和示例,我们把这2个middleware添加到Express中。
webpack配置文件部分
首先,修改webpack的配置文件(为了方便查看,这里贴出了webpack.config.js
的全部代码):
var webpack = require(''webpack'');
var path = require(''path''); var publicPath = ''http://localhost:3000/''; var hotMiddlewareScript = ''webpack-hot-middleware/client?reload=true''; var devConfig = { entry: { page1: [''./client/page1'', hotMiddlewareScript], page2: [''./client/page2'', hotMiddlewareScript] }, output: { filename: ''./[name]/bundle.js'', path: path.resolve(''./public''), publicPath: publicPath }, devtool: ''source-map'', module: { loaders: [{ test: /\.(png|jpg)$/, loader: ''url?limit=8192&context=client&name=[path][name].[ext]'' }, { test: /\.scss$/, loader: ''style!css?sourceMap!resolve-url!sass?sourceMap'' }] }, plugins: [ new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ] }; module.exports = devConfig;
这是一个包含多个entry的较复杂的例子。其中和webpack-hot-middleware有关的有两处。一是plugins
的位置,增加3个插件,二是entry
的位置,每一个entry后都增加一个hotMiddlewareScript
。
hotMiddlewareScript
的值是webpack-hot-middleware/client?reload=true
,其中?
后的内容相当于为webpack-hot-middleware设置参数,这里reload=true
的意思是,如果碰到不能hot reload的情况,就整页刷新。
在这个配置文件中,还有一个要点是publicPath
不是/
这样的值,而是http://localhost:3000/
这样的绝对地址。这是因为,在使用?sourceMap
的时候,style-loader会把css的引入做成这样:
这种blob
的形式可能会使得css里的url()
引用的图片失效,因此建议用带http
的绝对地址(这也只有开发环境会用到)。有关这个问题的详情,你可以查看github上的issue。
Express启动文件部分
接下来是Express启动文件内添加以下代码:
var webpack = require(''webpack''),
webpackDevMiddleware = require(''webpack-dev-middleware''), webpackHotMiddleware = require(''webpack-hot-middleware''), webpackDevConfig = require(''./webpack.config.js''); var compiler = webpack(webpackDevConfig); // attach to the compiler & the server app.use(webpackDevMiddleware(compiler, { // public path should be the same with webpack config publicPath: webpackDevConfig.output.publicPath, noInfo: true, stats: { colors: true } })); app.use(webpackHotMiddleware(compiler));
以上这段代码应该位于Express的routes代码之前。其中,webpack-dev-middleware配置的publicPath
应该和webpack配置文件里的一致。
webpack-dev-middleware和webpack-hot-middleware的静态资源服务只用于开发环境。到了线上环境,应该使用express.static()
。
到此,client
部分的目标就完成了。现在到网页里打开控制台,应该可以看到[HMR] connected
的提示。这个项目中我只要求css使用HMR,如果你希望javascript也使用HMR,一个简单的做法是在entry文件内添加以下代码:
if(module.hot) {
module.hot.accept(); }
这样,与这个entry相关的所有.js
文件都会使用hot reload的形式。关于这一点的更多详情,请参考hot module replacement。
接下来是server
部分。
reload和supervisor
server
部分的自动刷新,会面临一个问题:自动刷新的消息通知依靠的是浏览器和服务器之间的web socket连接,但在server
部分修改代码的话,一般都要重启服务器来使变更生效(比如修改routes
),这就会断开web socket连接。
所以,这需要一个变通的策略:浏览器这边增加一个对web socket断开的处理,如果web socket断开,则开启一个稍长于服务器重启时间的定时任务(setTimeout
),相当于等到服务器重启完毕后,再进行一次整页刷新。
reload是一个应用此策略的组件,它可以帮我们处理服务器重启时的浏览器刷新。
现在,还差一个监听server
文件,如果有变更就重启服务器的组件。参考reload的推荐,我们选用supervisor。
下面将reload和supervisor引入到Express项目内。
监听文件以重启服务器
通过以下代码安装supervisor
(是的,必须-g
):
npm install supervisor -g
然后,在package.json
里设置新的scripts
:
"scripts": {
"start": "cross-env NODE_ENV=dev supervisor -i client app"
}
这里的主要变化是从node app
改为supervisor -i client app
。其中-i
等于--ignore
,这里表示忽略client
,显然,我们可不希望在改前端代码的时候服务器也重启。
这里的cross-env
也是一个npm组件,它可以处理windows和其他Unix系统在设置环境变量的写法上不一致的问题。
把会重启的服务器和浏览器关联起来
把Express启动文件最后的部分做这样的修改:
var reload = require(''reload'');
var http = require(''http''); var server = http.createServer(app); reload(server, app); server.listen(3000, function(){ console.log(''App (dev) is now running on port 3000!''); });
Express启动文件的最后一般是app.listen()
。参照reload的说明,需要这样用http
再增加一层服务。
然后,再到Express的视图文件views里,在底部增加一个<script>
:
<% if (env !== "production") { %> <script src="/reload/reload.js"></script> <% } %>
所有的views都需要这样一段代码,因此最好借助模板引擎用include或extends的方式添加到公共位置。
这里的reload.js
和前面webpack的开发环境bundle.js
并不冲突,它们一个负责前端源文件变更后进行编译和刷新,另一个负责在服务器发生重启时触发延时刷新。
到此,server
也完成了。现在,修改项目内的任意源文件,按下ctrl + s
,浏览器里的页面都会对应地做一次“适当”的刷新。
完整示例
完整示例已经提交到github:express-webpack-full-live-reload-example
效果如下:
附加的可选方案
前面说的server
部分,分为views和routes,如果只修改views,那么服务器并不需要重启,直接刷新浏览器就可以了。
针对这样的开发情景,可以把views文件的修改刷新变得更快。这时候我们不用reload和supervisor,改为用browsersync,在Express的启动文件内做如下修改:
var bs = require(''browser-sync'').create();
app.listen(3000, function(){ bs.init({ open: false, ui: false, notify: false, proxy: ''localhost:3000'', files: [''./server/views/**''], port: 8080 }); console.log(''App (dev) is going to be running on port 8080 (by browsersync).''); });
然后,使用browsersync提供的新的访问地址就可以了。这样,修改views(html)的时候,由browsersync帮忙直接刷新,修改css和javascript的时候继续由webpack的middleware来执行编译和刷新。
转载:https://segmentfault.com/a/1190000004505747
express中间件
1.中间件技术
百度百科中的解释:中间件技术可以理解为是处于操作系统和应用程序之间的软件。人们在使用中间件时,往往是一组中间件集成在一起,构成一个平台,在这组中间中必须要有一个
通信中间件,即中间件=平台+通信 . 这个定义也限定了只有用于分布式系统中才能成为中间件。
简单的说中间件技术,就是介于底层软硬件和应用层之间的"中介"服务.这些服务具有标准的程序接口和协议.针对不同底层软硬件和应用层中的软件有符合接口和协议的多种实现。
说到这里,大家应该知道中间件的优点了吧:
中间件屏蔽了底层操作系统的复杂性,使得程序开发不必关心底层环境,让程序设计人员将更多的精力放在业务上,也减少了后期代码维护运行管理的工作量。
2.express的中间件
express应用程序本质上是一系列中间件函数调用. 下一个中间件函数由名为变量next表示。
中间件的类型
应用程序级中间件:
通过使用函数将程序级中间件绑定到app对象实例,处理http请求方法。
app. use()中有2个参数,第1个参数可以是路径、路由、挂载点,第2个是回调函数。
(1)没有装载路径的中间件功能。每次应用程序收到请求时都会执行该功能。
(2)/user/:id
路径上安装的中间件功能。
(3)路由及其处理函数绑定中间件。该函数处理对/user/:id
路径的GET请求。
路由器级中间件:
路由器级中间件的工作方式和应用程序及中间件的工作方式不同,它绑定到express.Router()实例化的对象中。
使用router.use()
和router.METHOD()
函数加载路由器级中间件。
错误处理中间件:
错误处理中间件必须四个参数。err, req, res, next。即使您不需要使用该next
对象,也必须写上。
否则,该next
对象将被解释为常规中间件,并且将无法处理错误。
内置中间件:
- express.static提供静态资源,如HTML文件,图像等。
- express.json使用JSON有效负载解析传入的请求。注意:适用于Express 4.16.0+
- express.urlencoded用URL编码的有效负载解析传入的请求。 注意:适用于Express 4.16.0+
第三方中间件:
使用第三方中间件,express提供更多的接口,为应用程序添加功能。
先下载安装第三方中间件,然后引入到所要使用的中间件中。
今天先总结到这里,才疏学浅,先沉淀沉淀在输出。
总结
以上是小编为你收集整理的express中间件全部内容。
如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。
原文地址:https://www.cnblogs.com/xixinhua/p/10265105.html
express中间件加载机制示例详解
前言
作为node web 框架的鼻祖,express和koa 是每个写node的同学都会使用的两个框架,那么两个框架在中间件的加载机制上有什么区别?koa的洋葱模型到底是什么?midway在这两个框架之上又做了怎么样的封装?本文将带走进这个一点儿都不神奇的世界~
express 中间件加载
众所周知,express定义中间件的时候,使用use方法即可,那么use方法到底做了些什么呢?让笔者带你来扒一扒源码。 github.com/expressjs/e… 由于原始代码较长,这里小编就拆开分解来解读。
var offset = 0; var path = ''/''; // default path to ''/'' // disambiguate app.use([fn]) if (typeof fn !== ''function'') { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) { arg = arg[0]; } // first arg is the path if (typeof arg !== ''function'') { offset = 1; path = fn; } } var fns = flatten(slice.call(arguments, offset)); if (fns.length === 0) { throw new TypeError(''app.use() requires a middleware function'') }
这部分对应源码的195-218行,主要是获取需要执行的function,以及区分,传入的是中间件,还是路由。 通过源码可知,用户在传入的第一个参数,如果不是function,则会判断是不是数组,如果是数组的情况下,就会判断数组的第0项是不是function,这部分逻辑是做什么呢? 这部分是对入参的兼容,因为express的入参可以有多种形式,如下:
app.use(''/users'', usersRouter); app.use([function (req, res, next) { console.log(''middleware 1....''); next(); }, function (req, res, next) { console.log(''middleWare 2....''); next(); }]) // catch 404 and forward to error handler app.use(function (req, res, next) { next(); next(createError(404)); });
用户可以传入多中间件,也可以传入单中间件,以及传入路由。这部分代码就是对这几种情况的区分,明确之后用户传入的内容到底是什么,然后再对其进行针对性的处理。
// setup router this.lazyrouter(); var router = this._router;
这一部分是路由的准备工作,由于use方法允许用户创建路由,则需要在对其进行处理之前,先初始化路由。这部分暂时不详细展开说,待有缘再进行详细讲解。
接下来就是中间件的的详细处理逻辑
fns.forEach(function (fn) { // non-express app if (!fn || !fn.handle || !fn.set) { return router.use(path, fn); } debug(''.use app under %s'', path); fn.mountpath = path; fn.parent = this; // restore .app property on req and res router.use(path, function mounted_app(req, res, next) { var orig = req.app; fn.handle(req, res, function (err) { setPrototypeOf(req, orig.request) setPrototypeOf(res, orig.response) next(err); }); }); // mounted an app fn.emit(''mount'', this); }, this);
这里第一个if中的判断就很有意思,如果fn不存在,或者不存在fn.handle, 或者不存在fn.set,那么就会直接return router.use(path, fn);
那么什么情况下会发生这种情况呢?好像我们上边写的中间件,路由都满足这种情况,难不成中间件就是路由?而实际执行debug的时候,也确实发现,所有的我们定义的中间件,都走了return router.use(path, fn);这个方法,很神奇。
而什么情况下会走到下边的方法呢?
当传入的function具有handle和set方法时,则会认为执行下边的方法,同样也是执行router.use();
事已至此,如果不了解router到底做了什么,是不可能弄明白中间件加载机制了,好吧,那么我们就顺藤摸瓜,前来看看router模块都做了些什么事情吧。
书接上文,app.lazyrouter 是对路由进行初始化,详细代码如下
app.lazyrouter = function lazyrouter() { if (!this._router) { this._router = new Router({ caseSensitive: this.enabled(''case sensitive routing''), strict: this.enabled(''strict routing'') }); this._router.use(query(this.get(''query parser fn''))); this._router.use(middleware.init(this)); } };
寿险判断 _router是否存在,防止重复创建。
接下来就是router定义的详细逻辑
var proto = module.exports = function(options) { var opts = options || {}; function router(req, res, next) { router.handle(req, res, next); } // mixin Router class functions setPrototypeOf(router, proto) router.params = {}; router._params = []; router.caseSensitive = opts.caseSensitive; router.mergeParams = opts.mergeParams; router.strict = opts.strict; router.stack = []; return router; };
初始化router对象,并且对其进行初始化赋值。针对上文中使用的router.use,我们来看看其具体都做了什么吧。
由于use方法较长,我们也是拆分开来进行探索。
var offset = 0; var path = ''/''; // default path to ''/'' // disambiguate router.use([fn]) if (typeof fn !== ''function'') { var arg = fn; while (Array.isArray(arg) && arg.length !== 0) { arg = arg[0]; } // first arg is the path if (typeof arg !== ''function'') { offset = 1; path = fn; } } var callbacks = flatten(slice.call(arguments, offset));
这部分代码与app.use代码基本上是一致的,只是最后一个函数改了名字。这里就不再进行详细赘述。
接下来就是重中之重了
for (var i = 0; i < callbacks.length; i++) { var fn = callbacks[i]; if (typeof fn !== ''function'') { throw new TypeError(''Router.use() requires a middleware function but got a '' + gettype(fn)) } // add the middleware debug(''use %o %s'', path, fn.name || ''<anonymous>'') var layer = new Layer(path, { sensitive: this.caseSensitive, strict: false, end: false }, fn); layer.route = undefined; this.stack.push(layer); }
这部分代码对于传入的函数进行了遍历,然后对每一个function都新建了一个layer层。然后将layer放入了栈中,如果不出意外在真正调用的时候,将会执行遍历这个栈中的所有layer,然后对其进行遍历执行。
function Layer(path, options, fn) { if (!(this instanceof Layer)) { return new Layer(path, options, fn); } debug(''new %o'', path) var opts = options || {}; this.handle = fn; this.name = fn.name || ''<anonymous>''; this.params = undefined; this.path = undefined; this.regexp = pathRegexp(path, this.keys = [], opts); // set fast path flags this.regexp.fast_star = path === ''*'' this.regexp.fast_slash = path === ''/'' && opts.end === false }
layer代码相对简单,定义了handle和regexp,并且设置了两个快速检索的flag。
那么真正调用的时候真的如我们想象的那样吗?真正的url请求来了以后express是如何处理的呢?
express在处理请求时,寿险调用的是express app 的handle方法,该方法比较简单,核心逻辑是调用router.handle(req, res, done)
方法,接下来我们就一起扒一扒route的handle方法吧~这段二百行的代码,究竟做了些什么?好吧,代码行确实太多了,相信你也不愿因看我的流水账,接下来我就将代码进行一下归纳吧
proto.handle = function handle(req, res, out) { var self = this; var idx = 0; // middleware and routes var stack = self.stack; req.next = next; next(); function next(err) { var layer; var match; var route; // 找到match的layer while (match !== true && idx < stack.length) { layer = stack[idx++]; match = matchLayer(layer, path); route = layer.route; if (match !== true) { continue; } if (!route) { // process non-route handlers normally continue; } } // this should be done for the layer self.process_params(layer, paramcalled, req, res, function (err) { if (err) { return next(layerError || err); } if (route) { // 执行layer的handle_request方法,其实就是中间件传入的函数 return layer.handle_request(req, res, next); } trim_prefix(layer, layerError, layerPath, path); });
这个方法的原理其实很简单,初始化idx=0,然后while循环找到第一个match的方法,就是我们定义的中间件/路由,然后执行相对应的function。
matchLayer方法中用到了layer初始化的时候定义的this.regexp.fast_slash变量
if (this.regexp.fast_slash) { this.params = {} this.path = '''' return true } -------------------- this.regexp.fast_slash = path === ''/'' && opts.end === false
通过这个代码以及fast_flash定义,以及上边path的定义我们可以知道,我们初始化的中间件,全部都是以var path = ''/'';
的方式存储的,layer初始化时传入的end=false, 所以中间件的 this.regexp.fast_slash = true,即所有的中间件在所有的路由下都会执行。
按照这个执行逻辑,如果我们自定义一个path=''/''的路由,是不是也都会执行呢?以及如果出现两个相同名字的路由,会怎么处理呢?按照这个推论,我测试了如下代码
app.use(''/'', function (req, res, next) { console.log(''hello world''); next() }); app.use(''/users'', function (req, res, next) { console.log(''/users-------''); next(); }); //hello world // /users------- //GET /users 304 1.280 ms - -
试验结果与我们的推论一致。
然而,当我在测试的时候,发现中间件写的位置也会有影响,写在router之后的中间件就不会被执行到,这个是什么原因呢? 通过看源码发现,在路由处理时,执行了res.send ,之后并未执行next()命令,导致其之后的代码并未执行。
总结: express使用use方法加载中间件,中间件和路由以layer的形式保存到stack中,待真正需要使用的时候,再对其进行遍历,找到真正需要用到的中间件和路由。我们还可以通过路由的加载顺序,拦截路由。
好吧,硬肝了两天,终于把express的中间件加载机制给肝完了,逻辑层层深入,柳暗花明,其中还有很多地方值得深思,比如app.use方法那里传入的到底还能是什么呢?留给有兴趣的读者深入研究吧。
总结
到此这篇关于express中间件加载机制的文章就介绍到这了,更多相关express中间件加载内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
- 浅谈express 中间件机制及实现原理
- Node Express用法详解【安装、使用、路由、中间件、模板引擎等】
- express.js中间件说明详解
- nodejs开发——express路由与中间件
我们今天的关于详解vue+webpack+express中间件接口使用和vue 中间件的分享就到这里,谢谢您的阅读,如果想了解更多关于express.js中间件说明详解、express_webpack自动刷新、express中间件、express中间件加载机制示例详解的相关信息,可以在本站进行搜索。
本文标签: