浅析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对象)以实现分级路由。其间的关系可由下图表示:

考察如下代码:

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 Uri 1();
  })
.delete(function(req, res) {
    handleDeleteUri 1();
  });

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表中的数据
function queryMysqlData(req, res, next) {
    if ("id" in req.params) {
        dbpool.query(" select * from articles where id=?" ,[req.params.id], function (err, rows, fields) {
            if (err) {
                res.send(err.stack);
            } else {
                if (rows && rows.length > 0) {
                    console.log('The queried rows is: ', rows.length);
                    res.articles = rows;
                    next();
                } else {
                    res.send("no query results!");
                }
            }
        });
    } else {
        res.send("invalid query params!");
    }
}
 
//处理查询mysql后得到的数据
function handleQueryData(req, res) {
    if ("articles" in res) {
        res.send("id:" + req.params.id + ";title:" + res.articles[0].title);
    }else{
        res.send("no query data handled!");
    }
}

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/

原文地址:http://blog.51cto.com/solarboy/2144886

时间: 2024-10-13 02:04:27

浅析Express中的路由与应用模式的相关文章

express中的路由

一.读取静态文件 基本代码: "use strict"; const express = require("express"); let app = express(); //template是文件夹名 app.use("/www", express.static("template")); //app.use(express.static("template")); app.listen(8888, ()

辛星浅析VMware中的几种联网模式

首先是桥接模式,也就是bridge,在该模式下,VMware虚拟出来的操作系统就像是局域网中的一台独立的主机,它可以访问网内任何一台机器,不过我们需要多于一个的IP地址,并且需要手工为虚拟系统配置IP地址子网掩码,而且还要和宿主机器处于同一网段,这样虚拟系统才能够和宿主机器进行通信.如果我们想用VMware在局域网内新建一个虚拟服务器,为局域网内提供网络服务,那么我们应该选择桥接模式. 对于nat模式,也就是网络地址转换模式,就是让虚拟系统借助NAT(网络地址转换)功能,通过宿主机器所在的网络来

浅析Web开发中前端路由实现的几种方式

故事从名叫Oliver的绿箭虾`说起,这位大虾酷爱社交网站,一天他打开了 Twitter ,从发过的tweets的选项卡一路切到followers选项卡,Oliver发现页面的内容变化了,URL也变化了,但为什么页面没有闪烁刷新呢?于是Oliver打开的网络监控器(没错,Oliver是个程序员),他惊讶地发现在切换选项卡时,只有几个XHR请求发生,但页面的URL却在对应着变化,这让Oliver不得不去思考这一机制的原因… 叙事体故事讲完,进入正题.首先,我们知道传统而经典的Web开发中,服务器端

Web开发中 前端路由 实现的几种方式和适用场景

浅析Web开发中前端路由实现的几种方式 主题 Web开发 故事从名叫Oliver的绿箭虾`说起,这位大虾酷爱社交网站,一天他打开了 Twitter ,从发过的tweets的选项卡一路切到followers选项卡,Oliver发现页面的内容变化了,URL也变化了,但为什么页面没有闪烁刷新呢?于是Oliver打开的网络监控器(没错,Oliver是个程序员),他惊讶地发现在切换选项卡时,只有几个XHR请求发生,但页面的URL却在对应着变化,这让Oliver不得不去思考这一机制的原因- 叙事体故事讲完,

Express中文文档

Express 4.x API express 翻译 api文档 中文 -- express() express()用来创建一个Express的程序.express()方法是express模块导出的顶层方法. var express = require('express'); var app = express(); Methods express.static(root, [options]) express.static是Express中唯一的内建中间件.它以server-static模块为

express中间件和路由教程

一.路由1.通常HTTP URL的格式是这样的:http://host[:port][path] http表示协议. host表示主机. port为端口,可选字段,不提供时默认为80. path指定请求资源的URI(Uniform Resource Identifier,统一资源定位符),如果URL中没有给出path,一般会默认成"/"(通常由浏览器或其它HTTP客户端完成补充上). 所谓路由,就是如何处理HTTP请求中的路径部分.比如"http://xxx.com:80/u

精华 对express中next函数的一些理解

关于next主要从三点来进行说明: next的作用是什么? 我们应该在何时使用next? next的内部实现机制是什么? Next的作用 我们在定义express中间件函数的时候都会将第三个参数定义为next,这个next就是我们今天的主角,next函数主要负责将控制权交给下一个中间件,如果当前中间件没有终结请求,并且next没有被调用,那么请求将被挂起,后边定义的中间件将得不到被执行的机会. 何时使用Next 从上边的描述我们已经知道,next函数主要是用来确保所有注册的中间件被一个接一个的执

Yaf零基础学习总结8-Yaf中的路由和路由协议

路由器主要负责解析一个请求并且决定什么module.controller.action被请求:它同时也定义了一种方法来实现用户自定义路由,这也使得它成为最重要的一个MVC组组件.为了方便自定义路由, Yaf摒弃了0.1版本中的自定义路由器方式, 而采用了更为灵活的路由器和路由协议分离的模式.也就是一个固定不变的路由器, 配合各种可自定义的路由协议, 来实现灵活多变的路由策略. 作为一个应用中的路由组件是很重要的,理所当然的路由组件是抽象的,这样允许作为开发者的我们很容易的设计出我们自定义的路由协

Android中的系统服务(代理模式)

一,系统启动 Android设备的开机流程总得来分可以分为三部分: 加载引导程序 引导程序bootloader是开机运行的第一个小程序,因此它是针对特定的主板与芯片的.bootloader有很多种,可以使用比较流行的如redboot.uboot.ARMBoot等,也可以开发自己的引导程序,它不是Android操作系统的一部分.引导程序也是OEM厂商或者运营商加锁和限制的地方. 引导程序初始化硬件设备.创建存储器空间的映射等软件运行时所需要的最小环境:加载Linux内核镜像文件(本文只针对Andr