浅析Express中的路由与应用模式

2019-08-19 10:24:29

参考地址 浅析Express中的路由与应用模式

1.   引言

        Express是一个基于Node.js的轻量级web开发框架,具有体积小,使用灵活等特点。查看Express的源码,如果不计供使用的中间件,主体框架只有一千余行代码,非常简练。

        Express模型的核心为Express中定义的路由和路由器。分析Express源码可发现Express的路由提供多种灵活的应用模式。

         我们首先介绍一下Express中的路由、路由器相关概念、结构及其特点,然后针对典型场景描述使用Express路由的四种应用模式。

2. Express中的路由与路由器

        Express具有典型的MVC模型特征。我们将路由定义为一个二元组route=(path, endpoint):其中path为HTTP请求的路径,endpoint为请求路径应映射到的端点(端点可视为处理该请求的实体),则Express中的路由器负责将请求映射到对应端点进行处理。

       Express中的路由器分为两种类型:

       app类型的路由器常使用如下代码创建:


var express =require('express');

var app = express();


        router类型的路由器常使用如下代码创建:


  var express = require('express');

var router =express.Router();


          app和router是形为function(request, response, next)形式的函数对象,使用app.verb(),router.verb()形式函数实现路由注册(路由注册本质上是一个观察者模式)。

        app.verb()和router.verb()中的verb常使用use、get、post、put、delete、route等动词,不同动词管辖的HTTP请求方法范围不同,这些动词函数的参数形式常为(pathExp, handleCallback)形式:其中pathExp表示请求路径,可为正则表达式;handleCallback为路径映射处理函数。

       app是Express框架所构建程序的请求处理入口,app可作为顶层路由器使用,在应用中也可挂载下级路由器(使用router对象)以实现分级路由。其间的关系可由图1表示:


图 1

        考察如下代码:


app.use('/reports', router1);

router1.get('/querymysql/:id', queryMysqlData,handleQueryData);


        对于http请求URL“/reports/querymysql/1”,Express中的路由器将此请求路由到queryMysqlData函数处理。

3.   Express中的路由应用模式

3.1.  REST模式

        对于一个使用restful风格的应用, 让我们想一想在不使用Express的时候如何在Node.js中处理rest请求,我们常常会写下如下示例代码:


var server = http.createServer(function (request,response) {

     switch(request.url) {

                   case 'uri1'

                       handleUri1 (request, response);

                       break;

                   case ' uri2'

                       handleU ri2(request, response);

                       break;

                   case ' uri3'

                       handleU ri3 (request, response);

                       break;

                   case ' uri4'

                       handleU ri4 (request, response);

                       break;

                   ...

                   default:

                       logToConsole('unknown path:' + path);

                       response.writeHead(404);

                       response.end("404 Not found");

                       break;

               }

}


        Express将上面代码中对每个rest资源的操作(switch分支)转换为路由,路由中的路径为rest资源的URI,处理端点为function(request, response,next)形式、对rest资源的操作函数。

        常使用app.route函数实现一个完整的restful接口,如下示例代码:


app.route('/uri1')

  .get(function(req,res) {

    handleGetUri1();

  })

.post(function(req, res) {

    handlePost Uri1();

  })

 .put(function(req, res) {

    handlePut Uri1();

  })

.delete(function(req, res) {

    handleDeleteUri1();

  });


3.2.  AOP模式

        在处理不同路径的HTTP请求时,常常需要在请求处理前和处理后做一些通用操作,这种应用需求是一个典型的AOP应用要求。

        Express中允许定义一个具有通配路径的路由,在调用其它路径的路由前会先调用该通配路径路由。此通配路径路由也成为其它路径路由切面的一个注入点,考察如下示例代码:


router.use(function timelog(req,res,next){

   console.log("receive report request time is:",Date.now());

    next(); //注意next函数的使用,必须声明该函数才能调用后继映射函数

});

router.get('/chart1', proxy({

    target:'http://127.0.0.1:8082',

   changeOrigin: true,

    pathRewrite:{

       '^/reports/chart1': '/loadChart1'

    }

})); 

router.get('/querymysql/:id', queryMysqlData,handleQueryData);


        上述代码中,在执行router的'/chart1'路由和'/querymysql/:id'路由之前都会执行timelog函数,在日志中记录当前路由执行时间。

3.3.   责任链模式

        在Node.js中,由于多使用异步函数,常会出现异步回调函数中嵌套异步回调函数的情形。当出现多重异步回调时,则代码会变得混乱和难以维护。

        考察一个应用场景:应用需要在数据库中进行多次查询,并对多次查询的结果综合处理。若使用数据库提供的异步查询接口,则需要在前一个查询操作的回调函数中进行下一个查询操作,若写在一个回调函数中,代码显臃肿。

        Express的一个路由可定义多个处理函数,这些处理函数可设计为链式调用,实现了责任链模式,考察如下代码:


app.get('/test',function(req,res,next){

    handle1();

    next();

},function(req,res,next){

    handle2();

    next();

},function(req,res,next){

   handle3();

});


        上述代码中:handle1, handle2, handle3构成了一个处理责任链“handle1->handle2->handle3”,通过next函数指引链式调用。

        Express中路由的责任链应用特性使得多重异步嵌套的代码变得清晰和优雅。

         针对本节开始提到的数据库查询应用场景,下面的示例代码展示了责任链模式的应用特点。


router.get('/querymysql/:id',queryMysqlData, handleQueryData);

//查询mysql表中的数据

functionqueryMysqlData(req, res, next) {

    if ("id" in req.params) {

        dbpool.query(" select * fromarticles where id=?" ,[req.params.id], function (err, rows, fields) {

            if (err) {

                res.send(err.stack);

            } else {

                if (rows && rows.length> 0) {

                    console.log('The queriedrows is: ', rows.length);

                    res.articles = rows;

                    next();

                } else {

                    res.send("no queryresults!");

                }

            }

        });

    } else {

        res.send("invalid queryparams!");

    }

}

//处理查询mysql后得到的数据

functionhandleQueryData(req, res) {

    if ("articles" in res) {

        res.send("id:" +req.params.id + ";title:" + res.articles[0].title);

    }else{

        res.send("no query datahandled!");

    }

}


3.4.  熔断器模式

        上节提到的责任链模式本质上是一个逐级调用模型。在分布式服务架构(微服务架构)中,深度调用常常需要考虑调用可达性问题,即需要考虑某级调用会否一直不响应。调用可达性问题常使用熔断器模式,即在调用端设置一个熔断器,熔断条件产生时,熔断器发生熔断,返回给调用方调用失败信息。

        考虑这样的应用场景:对于一些有处理时间要求的请求,当在指定时间内没有完成处理,需要向请求方返回处理失败信息。针对此应用场景,可在Express路由中设置超时熔断器,当处理超时,开启熔断器,通知请求方本次处理请求失败。

        上述应用场景可使用如下示例代码应对:


app.get('/circuit',function(req, res, next){

    var bt=setTimeout(function () {

       next('route'); //触发熔断

    },3000); //设置熔断时间为3

   res.breakTimer= bt;

   next();

},function(req,res,next){

    handle2();

    next();

},function(req,res,next){

   handle3();

   clearTimeout(res.breakTimer);//正常执行完毕,取消熔断定时器

});

app.get('/circuit ',function(req,res,next){

  if(!res.finished){//如果还没有响应,启动熔断

    //返回给调用者熔断信息

   res.send("breakCondition is true, notify the invoker.");

  }

});


4.   小结

        本文介绍了Express框架中路由和路由器的概念、结构和特点,并针对典型应用场景归纳了REST、AOP、责任链、熔断器四种应用模式,可用于应用开发中的一些常用场景。

5.  参考文献

1.http://expressjs.com/

2.http://www.runoob.com/




  • 2020-03-14 23:39:59

    vuetify和@nuxt/vuetify icon 之我见

    vuetify中v-icon,貌似默认支持 Material Design Icons, Material Icons, Font Awesome 4 and Font Awesome 5, 我自己单独引入了vuetify 用哪一个图标都没有问题。但是用了@nuxt/vuetify只能用mdi-home这样的。不知道因为啥。肯定是封装后,封装成一个了。 但是我修改vuetify的设置,哪一个图标也都能用。哎,不过多研究了。

  • 2020-03-16 15:57:53

    nuxtjs中单独引入Message组件的问题

    // 引入elementUIimport { Message } from 'element-ui';//由于Message组件并没有install 方法供Vue来操作的,是直接返回的,因此按照官方文档单独引入的方法是//会报错的,需要给 Message 添加 install 方法Message.install = function (Vue, options) {Vue.prototype.$message = Message}Vue.use(Message )//消息提示

  • 2020-03-16 16:03:20

    css的var()函数

     随着sass,less预编译的流行,css也随即推出了变量定义var函数。var()函数,就如同sass和less等预编译软件一样,可以定义变量并且进行对应的使用。

  • 2020-03-16 16:52:05

    对icomoon的误解,以及最快速的使用

    此时需要注意顶部第一个选项,Quick Usage,一定要打开,Enable Quick Usage,谁让咱英语不好呢,这个时候会出现一个css连接,直接引用就好了,就可以随意使用图标了,引入这一个css就能实现我们的功能,省区引入太多文件的烦恼,你可以在浏览器打开这个css,可以看到里面把我们所用的文件整成base64了。所以挺好用的。

  • 2020-03-17 09:47:05

    video标签视频不自动播放的问题

    添加 muted 属性,就可以通过地址栏进入网页的时候自动播放了,手机端还是有的有限制的,比如iphone浏览器,就不行,苹果手机为了保护用户的流量和用户的意愿,是禁止自动播放的,必须有手动触发。

  • 2020-03-17 14:21:31

    nuxt+pm2 自动化部署及打包后文件自动上传阿里云 oss(精华)

    部署nuxtjs,这一篇文章就够了,pm2 代码自动发布依赖于 git 工具,先将 ssh 密钥配置再你的代码仓库(github 或者 gitLab),具体操作自行 google 或者点击github 配置 ssh。 使用 ssh 密钥链接服务器 s $ ssh-copy-id root@1.2.3.4 # 把本机的 SSH 秘钥添加至服务器,配置成功后,以后就不需要再执行这条 SSH 命令了

  • 2020-03-18 21:15:34

    使用canvas画布解决百度地图自定义图层全球连续显示问题

        基于百度地图的Web API进行自定义图层叠加时,默认的图层只能叠加到全球范围以内,即经度范围为[-180, 180],而无法将图层叠加到默认的全球范围以外,即经度范围超出了[-180, 180]之后,经纬度坐标会自动回归到(0, 0),而导致在地图拖拽时全球以外无法连续显示想要的图层,此时可以基于百度地图的自定义图层将经纬度坐标转为像素点使用画布canvas来解决该问题。解决后效果如下图所示: ———————————————— 版权声明:本文为CSDN博主「宏伟杰作」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/u011284073/article/details/80549950