zoukankan      html  css  js  c++  java
  • 【Express系列】第2篇——主程序的改造

    上一篇对项目的目录结构和 app.js 等一些文件做了一些改造,然而那只是开始。

    接下来将做进一步的改造和完善。

    我们先看看几个主要的脚本文件,下面的代码是我稍微修改过并添加注释的,方便理解每句代码的意思。

     

    app.js:

    var express = require('express'),
        path = require('path'),
        favicon = require('serve-favicon'),
        logger = require('morgan'),
        cookieParser = require('cookie-parser'),
        bodyParser = require('body-parser'),
        routes = require('./routes/index'),
        users = require('./routes/users');
    
    //生成一个 express 实例
    var app = express();
    
    //指定 web 应用的标题栏小图标的路径为:/static/favicon.ico
    app.use(favicon(path.join(__dirname, 'static', 'favicon.ico')));
    //加载日志中间件
    app.use(logger('dev'));
    //加载解析 json 的中间件
    app.use(bodyParser.json());
    //加载解析 urlencoded 请求体的中间件
    app.use(bodyParser.urlencoded({ extended: false }));
    //加载解析 cookie 的中间件
    app.use(cookieParser());
    //设置 static 文件夹为存放静态文件的目录
    app.use(express.static(path.join(__dirname, 'static')));
    
    //路由控制器
    app.use('/', routes);
    app.use('/users', users);
    
    //捕获404错误,并转发到错误处理器
    app.use(function(req, res, next) {
        var err = new Error('Not Found');
        err.status = 404;
        next(err);
    });
    
    
    //错误处理器
    if (app.get('env') === 'development') {
        //开发环境下的错误处理器,返回完整的错误对象
        app.use(function(err, req, res, next) {
            res.status(err.status || 500);
            res.send({
    code: 0, message: err.message, error: err }); }); } app.use(
    function(err, req, res, next) { //生产环境下的错误处理器,只返回错误提示消息 res.status(err.status || 500); res.send({
    code: 0, message: err.message }); });
    //导出app实例供其他模块调用 module.exports = app;

    原本错误处理器里面调用了 res.render 方法,但它依赖于模版引擎。而前面我已经把模版引擎配置相关的代码移除掉了,所以这里改用 res.send 方法实现。

    代码里指定了网站小图标,记得将小图标文件放到对应位置,否则会报错,实在没有小图片就把那行代码注释掉吧。

     

    start.js(PS:也就是之前的 bin/www)

    #!/usr/bin/env node
    //上面一行表明此文件是 node 可执行文件
    
    
    var app = require('./app'),//引入 app.js 导出的 app 实例
        debug = require('debug')('test:server'),//引入 debug 模块,打印调试日志
        http = require('http'),//引入 http 模块,用以创建 http 服务
        port = process.env.PORT || '3000',//环境变量如果设置了端口号,就用环境变量设置的,否则使用默认值3000
        server = null;
    
    //设置端口号
    app.set('port', port);
    
    //创建 http 服务
    server = http.createServer(app);
    
    //监听端上面设置的口号
    server.listen(port);
    //绑定错误事件处理函数
    server.on('error', onError);
    //绑定监听事件处理函数
    server.on('listening', onListening);
    
    //错误事件处理函数
    function onError(error) {
        if (error.syscall !== 'listen') {
            throw error;
        }
    
        var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
    
        //对特定的错误类型做友好的处理
        switch (error.code) {
            case 'EACCES':
                console.error(bind + ' requires elevated privileges');
                process.exit(1);
                break;
            case 'EADDRINUSE':
                console.error(bind + ' is already in use');
                process.exit(1);
                break;
            default:
                throw error;
        }
    }
    
    //监听事件处理函数
    function onListening() {
        var addr = server.address(),
            bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
        
        debug('Listening on ' + bind);
    }

    本来在 start.js 里有一个 normalizePort 函数,用来做端口号转换的。但我觉得没有什么必要,只要在配置端口号的时候注意即可,没必要做这样的适配,于是将它移除,简化代码。

     

    routes/index.js 和 routes/users.js:

    //routes/index.js
    var
    express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.send('hello world'); }); module.exports = router;
    //routes/users.js
    var
    express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.send('respond with a resource'); }); module.exports = router;

    上面两段脚本,生成一个路由实例用来捕获访问主页的 GET 请求,导出这个路由并在 app.js 中通过 app.use 方法被加载。当访问对应的 url 时,就会执行相应的处理回调。

    index.js 本来也是使用了 res.render 方法,也被改成使用 res.send 方法了。原因和上面提到过的同样修改点相同,这里就不再重复。

     

    然而,目前的这种路由配置形式我觉得不怎么美丽,因为我们可以试想一下,假如应用比较复杂,路由配置也相应的更复杂,那么在 app.js 就会出现大量的路由配置相关代码...

    为了避免这种情况,导致 app.js 过于臃肿,于是我将路由配置相关的代码重新规划了一下。

    先看看改造后的目录结构(主要变动在 routes):

    本来 routes 目录下有多个 js 文件,现在变成只有一个 main.js 了,然后里面又多了一个 modules 目录,里面会有多个 js 文件,它们都是 main.js 的子模块。

    比如图中看到的两个 menuMod.js 和 userMod.js,分别是菜单以及用户相关的业务逻辑,以此规则划分模块。

    而 routes 目录下的 main.js 则是一个路由配置的主文件,通过引入 routes/modules 目录中的各个子模块,指定不同请求方式,不同 url 所对应的不同回调函数。

    改造过程中,根据目录结构的改变,还要对 app.js 作对应修改,下面看看修改后的代码...

    app.js

    var express = require('express'),
        path = require('path'),
        favicon = require('serve-favicon'),
        logger = require('morgan'),
        cookieParser = require('cookie-parser'),
        bodyParser = require('body-parser'),
        routes = require('./routes/main');
    
    //生成一个 express 实例
    var app = express();
    
    //指定 web 应用的标题栏小图标的路径为:/static/favicon.ico
    app.use(favicon(path.join(__dirname, 'static', 'favicon.ico')));
    //加载日志中间件
    app.use(logger('dev'));
    //加载解析 json 的中间件
    app.use(bodyParser.json());
    //加载解析 urlencoded 请求体的中间件
    app.use(bodyParser.urlencoded({ extended: false }));
    //加载解析 cookie 的中间件
    app.use(cookieParser());
    //设置 static 文件夹为存放静态文件的目录
    app.use(express.static(path.join(__dirname, 'static')));
    
    //配置路由
    routes(app);
    
    //捕获404错误,并转发到错误处理器
    app.use(function(req, res, next) {
        var err = new Error('Not Found');
        err.status = 404;
        next(err);
    });
    
    
    //错误处理器
    if (app.get('env') === 'development') {
        //开发环境下的错误处理器,将错误信息渲染 error 模版并显示到浏览器中
        app.use(function(err, req, res, next) {
            res.status(err.status || 500);
            res.render('error', {
                message: err.message,
                error: err
            });
        });
    }
    
    app.use(function(err, req, res, next) {
        //生产环境下的错误处理器,不会将错误信息泄露给用户
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: {}
        });
    });
    
    //导出 app 实例供其他模块调用
    module.exports = app;

    routes/main.js

    var _ = require('underscore'),//引入 underscore 模块
        userMod = require('./modules/userMod'),//引入 user 模块
        menuMod = require('./modules/menuMod'),//引入 menu 模块
        config = {
            get: {
                '/user/list': userMod.getUserList,
                '/user/status': userMod.checkStatus,
                '/menu/list': menuMod.getMenuList
            },
            post: {
                '/user/save': userMod.save,
                '/menu/save': menuMod.save
            }
        };//路由配置
    
    
    
    module.exports = function (app) {
    
        //分析路由配置对象,逐一处理
        _.each(config, function (subConfig, method) {
    
            _.each(subConfig, function (func, url) {
    
                app[method](url, func);
    
            });
    
        });
    
    };

    routes/modules/userMod.js

    var userMod = {
        getUserList: function (req, res) {
            res.send('userList');
        },
        checkStatus: function (req, res) {
            res.send('userStatus');
        },
        save: function (req, res) {
            res.send('userSave');
        }
    };
    
    module.exports = userMod;

    routes/modules/menuMod.js

    var menuMod = {
        getMenuList: function (req, res) {
            res.send('menuList');
        },
        save: function (req, res) {
            res.send('menuSave');
        }
    };
    
    module.exports = menuMod;

    上面的改造,还添加了一个项目依赖库,它就是知名的 underscore。

    打开命令行工具,进入项目根目录,执行下面指令,安装 underscore,并将它加入到 package.json 的 dependencies 之中:

    npm install underscore --save

    经过这样的改造后,以后修改路由规则,只需要在 routes/main.js 里修改 config 配置对象,然后根据需要添加新的子模块,或者在原有的子模块里加入新的方法即可。

    需要注意的是,每个子模块里的每一个方法,都有两个一样的参数 req 和 res,也就是 request 对象 和 response 对象。

    至于这两个对象都提供了什么方法,什么属性,可以去查 API,传送门:http://expressjs.jser.us/api

    OK,到目前为止,整个项目从创建到现在,已经面目全非了。

    但我居然跑都没跑过一次...是不是感觉很不靠谱?

    其实这个项目是根据之前的经验重新整理的,所以在弄的时候,基本就没测试是否能运行了。

    但为防百密一疏,或者说是谨慎起见,这里我们运行一下试试看。

    打开命令行工具,进入到项目根目录,然后运行下面指令:

    node start

    如果没看到报错信息,说明服务成功运行了。

    接下来,我们用浏览器访问 http://localhost:3000/user/list

    如果能看到浏览区里出现 userList,说明成功返回了

    再试试用浏览器访问 http://localhost:3000/user/status

    浏览区里出现 userStatus,OK,没问题了

    至此,我们的应用已经成功实现了根据不同的请求,做出相应相应的基本功能。后面可以根据需要慢慢完善细节了~

  • 相关阅读:
    HTTP 协议详解
    HTTP抓包工具之Fiddler
    C#网络编程(订立协议和发送文件)
    C#编程总结(十)字符转码
    通信协议:HTTP、TCP、UDP
    C# 对象、文件与二进制串(byte数组)之间的转换
    从输入URL 到页面加载完成的过程
    java动手动脑
    大道至简第二章读后感
    《大道至简》第一章 读后感
  • 原文地址:https://www.cnblogs.com/czf-zone/p/5226768.html
Copyright © 2011-2022 走看看