Express实用技巧和设计模式

Express实用技巧和设计模式

1.Express介绍

Express是一个简介、灵活的node.js web应用开发框架,是目前最流行的基于node.js的web开发框架,提供了一系列强大的功能,比如:

  • 路由控制
  • 中间件
  • 静态文件服务
  • 模板解析

本文主要介绍这些功能的使用和它的设计理念

2.Express如何使用

本地安装

$ npm install express

获取、引用通过变量app(app其实在内部是application返回的一个handle函数,所有express的方法都在app上的原型方法) 我们可以调用express的方法

var express = require('express)
var app = express()
app.liten(3000)

3.路由控制

express通过匹配请求路径,在做request、response操作,具体看下面get、post方法

  • Express的get方法

    第一个参数path为请求路径,第二个参数为处理请求的回调函数

    app.get(path, function(req, res))
    
    

    get方法使用

    const express = require('express');
    const app = express();
    //匹配htp://localhost:3000/hello做相关req,res的操作
    app.get('/hello',function(req,res){
        res.end('hello');
    });
    //匹配htp://localhost:3000/world做相关req,res的操作
    app.get('/world',function(req,res){
        res.end('world');
    });
    //匹配所有,主要用作not found
    app.get('*',function(req,res){
        res.setHeader('Content-Type','text/plain;charset=utf8');
        res.end('Not Found');
    });
    app.listen(3000);
    
    
  • Express的post方法

    第一个参数path为请求路径,第二个参数为处理请求的回调函数和get一样

    app.post(path,function(req,res))
    
    

    post方法使用

    var express = require('./express');
    var app = express();
    //匹配htp://localhost:3000/hello做相关req,res的操作
    app.post('/hello', function (req,res) {
       res.end('hello');
    });
    //匹配所有,主要用作not found
    app.post('*', function (req,res) {
        res.end('post没找到');
    });
    app.listen(3000);
    
    

    通过linux命令发送post请求

    $ curl -X POST http://localhost:3000/hello
    
    
  • Express的all方法

    监听所有的请求方法,可以匹配所有的HTTP动词。根据请求路径来处理客户端发出的所有请求,参数同上

    app.all(path,function(req, res))
    
    

    all方法使用

    const express = require('express');
    const app = express();
    app.all('/world',function(req,res){
        res.end('all world');
    });
    app.listen(3000);
    
    
  • Express Router的设计理念

    先看如下代码

    const express = require('express');
    const app = express();
    app.get('/user',function(req,res,next){
        console.log(1);
        next();
    },function(req,res,next){
        console.log(11);
        next();
    }).get('/world',function(req,res,next){
        console.log(2);
        next();
    }).get('/hello',function(req,res,next){
        console.log(3);
        res.end('ok');
    });
    app.listen(3000);
    
    

    如上代码,体现出express router的一个概念,就是二维数组的二维数据形式,这个概念的主要意义是:在router路由容器中存放一层层route实例,并且每层route实例中存放一层层callback,当匹配上一个route的时候,执行它里面的callback

    如下图所示

express-router.png

再router和route中分别用stack存储,不同的是Router中的stack存放的是Route,并且根据相同路由匹配,遍历Stack中相关Route,其中handle方法是挂载到layer上面的Route,并且触发Route从而遍历Route中的Stack,在Route中的Stack存放的是一层层的callback,所以最终调用所有callback在同一个匹配路径上,其中核心原理就是这个二维数组的二维数据形式

4.中间件

中间件就是处理HTTP请求的函数,用来完成各种特定的任务,比如检查用户是否登录、检测用户是否有权限访问等,它的特点是:

  • 一个中间件处理完请求和响应可以把相应数据再传递给下一个中间件

  • 回调函数的next参数,表示接受其他中间件的调用,函数体中的next(),表示将请求数据继续传递

  • 可以根据路径来区分返回执行不同的中间件

  • 1.中间件的使用

    主要通过use方法

    var express = require('express');
    var app = express();
    app.use(function (req,res,next) {
        console.log('全部匹配');
        next();
    });
    app.use('/water', function (req,res,next) {
        console.log('只匹配/water');
        next();
    });
    app.get('/water', function (req,res) {
        res.end('water');
    });
    app.listen(3000);
    
    

    2.中间件原理

    通过Application原型上的use方法,将Router变函数,抽象出Router方法复用,Router处理中间件,其实就是上面所讲述的路由控制原理,看如下代码,可以用app下面的use方法调取中间件,也可以创建一个express.router,在通过app下面use调用这个中间件,形成一个父子级别的中间件路由,下面user.use就是当访问/user/或者/user/2的子路由

    const express = require('../');
    const app = express();
    app.use(function(req,res,next){
        console.log('Ware1:',Date.now());
        next('wrong');
    });
    app.get('/',function(req,res,next){
        res.end('1');
    });
    const user = express.Router();
    user.use(function(req,res,next){
        console.log('Ware2',Date.now());
        next();
    });
    user.use('/2',function(req,res,next){
        res.end('2');
    });
    app.use('/user',user);
    app.use(function(err,req,res,next){
        res.end('catch '+err);
    });
    app.listen(3000,function(){
        console.log('server started at port 3000');
    });
    
    

    3.中间件设计模式

    主要思想就是Application有一个router属性指向了Router函数,在Router中返回一个router函数,将get/handle等方法挂载到返回的router上面,其实express.Router()方法就是Router函数。并且中间件和普通的路由都是在Router的stack中,如图所示

express-use.png

5.静态服务文件

如果要在网页中加载静态文件(css、js、img),就需要另外指定一个存放静态文件的目录,当浏览器发出非HTML文件请求时,服务器端就会到这个目录下去寻找相关文件

var express = require('express');
var app = express();
var path = require('path');
app.use(express.static(path.join(__dirname,'public')));
app.listen(3000);

  • 静态文件服务器实现

    static属于express内置中间件,其中原理主要是调用了serve-static库,具体实现是原生node.js API,可以查看我写的一篇如何搭建静态服务器 static-server

6.模板解析

这里主要说的是ejs模板,具体API请查阅 EJS官网

  • 安装ejs

    $ npm install ejs
    
    
  • 设置模板

    var express = require('express');
    var path = require('path');
    var app = express();
    app.set('view engine','ejs');
    app.set('views',path.join(__dirname,'views'));
    app.listen(3000);
    
    
  • 渲染html

    app.set('view engine','html')
    app.set('views',path.join(__dirname,'views'));
    app.engine('html',require('ejs').__express);
    
    
  • 渲染视图

    • 第一个参数 要渲染的模板
    • 第二个参数 渲染所需要的数据
    app.get('/', function (req,res) {
        res.render('hello',{title:'hello'},function(err,data){});
    });
    
    
  • 模板的实现

    res.render = function (name, data) {
        var viewEngine = engine.viewEngineList[engine.viewType];
        if (viewEngine) {
            viewEngine(path.join(engine.viewsPath, name + '.' + engine.viewType), data, function (err, data) {
                if (err) {
                    res.status(500).sendHeader().send('view engine failure' + err);
                } else {
                    res.status(200).contentType('text/html').sendHeader().send(data);
                }
            });
        } else {
            res.status(500).sendHeader().send('view engine failure');
        }
    }
    
    

7.结语

本篇文章主要介绍核心功能和核心代码思想,其余的方法如:redirect(重定向)、body-parser(请求体解析)、send方法等等不做介绍,具体请查阅下方给出的相关教程

8.博客

魏燃技术博客

有任何问题可留言或者发送本人邮箱ngaiwe@126.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容