• express 应用创建及app.js详解


    #1 express 应用创建

    1、安装node.js (自行百度)

    2、npm install express -g 

    3、全局安装express生成器 express-generator 

      npm install express-generator -g 

    4、查看 express 版本,可以检查生成器 express-generator  是否安装成功

       express -v 

    5、(可选)查看express 所有帮助指令及用法

      express -h

    6、cd 进入指定目录 workspace(任意命名)--------------这是系统cmd指令,不是node指令

    7、在指定目录 workspace(任意命名) 创建项目 nodejs-demo(任意命名)

      express -e nodejs-demo

    8、由当前目录 workspace(任意命名)  进入 项目目录 nodejs-demo(任意命名)--------------这是系统cmd指令,不是node指令

      cd workspace

    9、安装依赖

      npm install 

    10、启动项目

      npm start

    #2 目录结构# 接下来,我们详细看一下Express4项目的结构、配置和使用。

    bin, 存放启动项目的脚本文件

    node_modules, 存放所有的项目依赖库。

    public,静态文件(css,js,img)

    routes,路由文件(MVC中的C,controller)

    views,页面文件(Ejs模板)

    package.json,项目依赖配置及开发者信息

    app.js,应用核心配置文件

    输入图片说明

    #3 package.json项目配置# package.json用于项目依赖配置及开发者信息,scripts属性是用于定义操作命令的,可以非常方便的增加启动命令,比如默认的start,用npm start代表执行node ./bin/www命令。查看package.json文件。

    {
      "name": "express4-demo",
      "version": "0.0.0",
      "private": true,
      "scripts": {
        "start": "node ./bin/www"
      },
      "dependencies": {
        "body-parser": "~1.10.2",
        "cookie-parser": "~1.3.3",
        "debug": "~2.1.1",
        "ejs": "~2.2.3",
        "express": "~4.11.1",
        "morgan": "~1.5.1",
        "serve-favicon": "~2.2.0"
      }
    }
    

    #4 app.js核心文件# 从Express3.x升级到Express4.x,主要的变化就在app.js文件中。查看app.js文件,我已经增加注释说明。

    // 加载依赖库,原来这个类库都封装在connect中,现在需地注单独加载
    var express = require('express'); 
    var path = require('path');
    var favicon = require('serve-favicon');
    var logger = require('morgan');
    var cookieParser = require('cookie-parser');
    var bodyParser = require('body-parser');
    
    // 加载路由控制
    var routes = require('./routes/index');
    //var users = require('./routes/users');
    
    // 创建项目实例
    var app = express();
    
    // 定义EJS模板引擎和模板文件位置,也可以使用jade或其他模型引擎
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'ejs');
    
    // 定义icon图标
    app.use(favicon(__dirname + '/public/favicon.ico'));
    // 定义日志和输出级别
    app.use(logger('dev'));
    // 定义数据解析器
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    // 定义cookie解析器
    app.use(cookieParser());
    // 定义静态文件目录
    app.use(express.static(path.join(__dirname, 'public')));
    
    // 匹配路径和路由
    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);
    });
    
    // 开发环境,500错误处理和错误堆栈跟踪
    if (app.get('env') === 'development') {
        app.use(function(err, req, res, next) {
            res.status(err.status || 500);
            res.render('error', {
                message: err.message,
                error: err
            });
        });
    }
    
    // 生产环境,500错误处理
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: {}
        });
    });
    
    // 输出模型app
    module.exports = app;
    

    我们看到在app.js中,原来调用connect库的部分都被其他的库所代替,serve-favicon、morgan、cookie-parser、body-parser,默认项目中,只用到了最基本的几个库,还没有其他需要替换的库,在本文最后有详细列出。

    另外,原来用于项目启动代码也被移到./bin/www的文件,www文件也是一个node的脚本,用于分离配置和启动程序。查看./bin/www文件。

    #!/usr/bin/env node   
    
    /**
     * 依赖加载
     */
    var app = require('../app');
    var debug = require('debug')('nodejs-demo:server');
    var http = require('http');
    
    /**
     * 定义启动端口
     */
    var port = normalizePort(process.env.PORT || '3000');
    app.set('port', port);
    
    /**
     * 创建HTTP服务器实例
     */
    var server = http.createServer(app);
    
    /**
     * 启动网络服务监听端口
     */
    server.listen(port);
    server.on('error', onError);
    server.on('listening', onListening);
    
    /**
     * 端口标准化函数
     */
    function normalizePort(val) {
      var port = parseInt(val, 10);
      if (isNaN(port)) {
        return val;
      }
      if (port >= 0) {
        return port;
      }
      return false;
    }
    
    /**
     * HTTP异常事件处理函数
     */
    function onError(error) {
      if (error.syscall !== 'listen') {
        throw error;
      }
    
      var bind = typeof port === 'string'
        ? 'Pipe ' + port
        : 'Port ' + port
    
      // handle specific listen errors with friendly messages
      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();
      var bind = typeof addr === 'string'
        ? 'pipe ' + addr
        : 'port ' + addr.port;
      debug('Listening on ' + bind);
    }
    

    #5 Bootstrap界面框架# 创建Bootstrap界面框架,直接在index.ejs文件上面做修改。可以手动下载Bootstrap库放到项目中对应的位置引用,也可以通过bower来管理前端的Javascript库,参考文章 bower解决js的依赖管理另外还可以直接使用免费的CDN源加载Bootstrap的css和js文件。下面我就直接使用bower来管理前端的JavaScript库的方式。编辑views/index.ejs文件:

    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <title><%= title %></title>
        <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
        <link rel='stylesheet' href='/stylesheets/style.css' />
      </head>
      <body>
        <div class="well jumbotron">
          <h1><%= title %></h1>
          <p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
          <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
        </div>
        <script src="/bower_components/jquery/dist/jquery.min.js"></script>
        <script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
      </body>
    </html>
    

    效果如下,已经加入了bootstrap的样式了。

    输入图片说明

    接下来,我们把index.ejs页面切分成3个部分:header.ejs, index.ejs, footer.ejs,用于网站页面的模块化

    header.ejs, 为页面的头部区域

    index.ejs, 为内容显示区域

    footer.ejs, 为页面底部区域

    编辑header.ejs:

    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <title><%= title %></title>
        <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
        <link rel='stylesheet' href='/stylesheets/style.css' />
      </head>
      <body>
    

    编辑footer.ejs:

        <script src="/bower_components/jquery/dist/jquery.min.js"></script>
        <script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
      </body>
    </html>
    

    编辑index.ejs:

    <% include header.ejs %>
    
    <div class="well jumbotron">
    <h1><%= title %></h1>
    <p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
    <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
    </div>
    
    <% include footer.ejs %>
    

    把页表和页底的代码分离后,让index.ejs页面的核心代码更少,更容易维护。

    #6 路由功能# 路由功能,是Express4以后全面改版的功能。在应用程序加载隐含路由中间件,不用担心在中间件被装载相对于路由器中间件的顺序。定义路由的方式是不变的,路由系统中增加2个新的功能。

    app.route()函数,创建可链接的途径处理程序的路由路径。

    express.Router类,创建模块化安装路径的处理程序。

    app.route方法会返回一个Route实例,它可以继续使用所有的HTTP方法,包括get,post,all,put,delete,head等。

    app.route('/users')
      .get(function(req, res, next) {})
      .post(function(req, res, next) {})
    

    express.Router类,则可以帮助我们更好的组织代码结构。在app.js文件中,定义了app.use(‘/’, routes); routes是指向了routes目录下的index.js文件,./routes/index.js文件中,express.Router被定义使用,路径/*处理都会由routes/index.js文件里的Router来处理。如果我们要管理不同的路径,那么可以直接配置为多个不同的Router。

    app.use('/user', require('./routes/user').user);
    app.use('/admin', require('./routes/admin').admin);
    app.use('/', require('./routes'));
    

    #7 Ejs模板使用# 让ejs模板文件,使用扩展名为html的文件。修改:app.js

    app.engine('.html', ejs.__express);
    app.set('view engine', 'html');
    // app.set('view engine', 'ejs');
    

    修改后,ejs变量没有定义,supervisor的程序会一直报错

    ReferenceError: ejs is not defined
    at Object. (D:workspaceprojectnodejs-demoapp.js:17:21)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3
    DEBUG: Program node app.js exited with code 8
    

    在app.js中增加ejs变量:

    var express = require('express')
    , routes = require('./routes')
    , user = require('./routes/user')
    , http = require('http')
    , path = require('path')
    , ejs = require('ejs');
    

    #8 Session使用# session这个问题,其实是涉及到服务器的底层处理方式。像Java的web服务器,是多线程调用模型。每用户请求会打开一个线程,每个线程在内容中维护着用户的状态。

    像PHP的web服务器,是交行CGI的程序处理,CGI是无状态的,所以一般用cookie在客户的浏览器是维护用户的状态。但cookie在客户端维护的信息是不够的,所以CGI应用要模仿用户session,就需要在服务器端生成一个session文件存储起来,让原本无状态的CGI应用,通过中间文件的方式,达到session的效果。

    Nodejs的web服务器,也是CGI的程序无状态的,与PHP不同的地方在于,单线程应用,所有请求都是异步响应,通过callback方式返回数据。如果我们想保存session数据,也是需要找到一个存储,通过文件存储,redis,Mongdb都可以。

    接下来,我将演示如何通过mongodb来保存session,并实现登陆后用户对象传递。

    app.js文件:

    var express = require('express')
    , routes = require('./routes')
    , user = require('./routes/user')
    , http = require('http')
    , path = require('path')
    , ejs = require('ejs')
    , SessionStore = require("session-mongoose")(express);
    
    var store = new SessionStore({
        url: "mongodb://localhost/session",
        interval: 120000
    });
    
    ....
    app.use(express.favicon());
    app.use(express.logger('dev'));
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser());
    app.use(express.cookieSession({secret : 'fens.me'}));
    
    app.use(express.session({
        secret : 'fens.me',
        store: store,
        cookie: { maxAge: 900000 }
    }));
    
    app.use(function(req, res, next){
        res.locals.user = req.session.user;
        next();
    });
    app.use(app.router);
    app.use(express.static(path.join(__dirname, 'public')));
    

    注:app.js文件有顺序要求,一定要注意!!!

    安装session-mongoose依赖库:

    D:workspaceprojectnodejs-demo>npm install session-mongoose
    D:workspaceprojectnodejs-demonode_modulessession-mongoosenode_modulesmongoosenode_modulesmongodbnode_modulesbson>node "D:toolkitnodejsnode_modulesnpmbinnode-gyp-bin\....node_modulesnode-gypbinnode-gyp.js" rebuild
    C:Program Files (x86)MSBuildMicrosoft.Cppv4.0Microsoft.Cpp.InvalidPlatform.Targets(23,7): error MSB8007: 项目“kerberos.vcxproj”的平台无效。平台为“x64”。您会看到此消息的可能原因是,您尝试在没有解决方案文件的情况下生成项目,并且为
    oosenode_modulesmongoosenode_modulesmongodbnode_modulesbsonbuildbson.vcxproj]
    session-mongoose@0.2.2 node_modulessession-mongoose
    └── mongoose@3.6.10 (mpath@0.1.1, ms@0.1.0, hooks@0.2.1, sliced@0.0.3, muri@0.3.1, mpromise@0.2.1, mongodb@1.3.3)
    

    安装有错误但是没关系。访问:http://localhost:3000/login,正常

    修改routes/index.js文件,exports.doLogin方法:

    exports.doLogin = function(req, res){
        var user={
            username:'admin',
            password:'admin'
        }
        if(req.body.username===user.username && req.body.password===user.password){
            req.session.user=user;
            return res.redirect('/home');
        } else {
            return res.redirect('/login');
        }
    };
    

    exports.logout方法:

    exports.logout = function(req, res){
        req.session.user=null;
        res.redirect('/');
    };
    

    exports.home方法:

    exports.home = function(req, res){
        res.render('home', { title: 'Home'});
    };
    

    这个时候session已经起作用了,exports.home的user显示传值已经被去掉了。 是通过app.js中app.use的res.locals变量,通过框架进行的赋值。

    app.use(function(req, res, next){
        res.locals.user = req.session.user;
        next();
    });
    

    注:这个session是express3.0的写法,与express2.x是不一样的。原理是在框架内每次赋值,把我们刚才手动传值的过程,让框架去完成了

      

  • 相关阅读:
    java冒泡算法
    java时间操作
    Java重写构造方法
    正则Sub用法
    Python正则反向引用
    Django发送邮件
    Django导出excel
    Nginx编译安装
    年薪20万Python工程师进阶(7):Python资源大全,让你相见恨晚的Python库
    Go语言学习笔记
  • 原文地址:https://www.cnblogs.com/qinlongqiang/p/11444882.html
Copyright © 2020-2023  润新知