NodeJS_08

NodeJS七天课程学习笔记_第8天 Blog综合案例

Blog 综合案例 (包含注册、登录、修改密码、注销、发布、分页列表、评论、个人中心、上传头像等)

课程内容概要:

1. 介绍art-template中的 子模板 与 模板继承

2. 介绍表单同步提交与异步提交的区别

3. 介绍了可视化管理工具MongoBooster

4. 介绍了blueimp/javascript-MD5的配置和使用

5. 介绍express-session的配置与使用

6. 注册、登录 与 退出 功能的实现

7. 介绍 Express 中间件 原理

8. express中的 三大类 中间件

9. express中的 全局统一错误处理

 

注意: Node中有许多第3方模板引擎,不只有art-template,还有:

ejs (e代表effective高效之意,从json中生成html的一种魔法) 、

jade(因版权问题,后改名pug哈巴狗)、

handlebars(号称logic-less templateheima的manmanmai项目用到)、

nunjucks(是Mozilla开发的一个纯JavaScript编写的模板引擎)

首先讲art-template中的子模板(include) 以及 模板继承(extend)语法

官方文档地址 aui.github.io/art-template/zh-cn/docs/syntax.html

第1步, 新建下面4个文件 作为将来 被包含的子模板

公共的顶部public_top.html、

公共的底部public_footer.html、

公共的左侧边栏public_left.html、

公共的右广告栏public_right.html

第2步,新增 node_51_base_layout.html 作为将来被各个实际页面继承(extend)的母板

母板里面有个完整的骨架,

母板里包含(include)了公共的顶部、底部、左侧边栏、右广告栏

母板里head引入所有页面要用到的公共的css(如bootstrap)、js(如jquery)

同时,母板里,

通过block语法,在head标签中 预留了 实际页面真正要用到的 title、css、 js空间

通过block语法,在body标签中 预留了 实际页面真正要用到的html 空间

node_51_base_layout.html母板完整代码如下:

<!DOCTPYE html>  
<html lang="zh">  
<head>  
    <link rel="icon" href="public/img/beyond.jpg" type="image/x-icon"/>
    <meta charset="UTF-8">
    <meta name="author" content="beyond">
    <meta http-equiv="refresh" content="520">
    <meta name="description" content="未闻花名-免费零基础教程-beyond">
    <meta name="viewport" content="width=device-width, 
    initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" />
    <meta name="keywords" content="HTML,CSS,JAVASCRIPT,JQUERY,XML,JSON,C,C++,C#,OC,PHP,JAVA,JSP,PYTHON,RUBY,PERL,LUA,SQL,LINUX,SHELL,汇编,日语,英语,泰语,韩语,俄语,粤语,阿语,魔方,乐理,动漫,PR,PS,AI,AE">
    <!--[if lt IE 9]>
        <script src="//apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
        <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js">
        </script>
        <![endif]-->

    <!-- 公共的样式 -->
    <style type="text/css">
        body{
            font-size: 100%; 
            /*声明margin和padding是个好习惯*/  
            margin: 0;  
            padding: 0; 
            background-image: url("public/img/sakura4.png");  
            background-repeat: no-repeat;  
            background-position: center center;  
        }
    </style>
    <!-- 公共的样式 -->
    <link rel="stylesheet" type="text/css" href="public/css/beyondbasestylewhite5.css">
    <!-- 绿色按钮的css效果 -->
    <link rel="stylesheet" type="text/css" href="public/css/beyondbuttongreen.css">        

    <!-- 公共的JS特效 -->
    <script type="text/javascript" src="public/js/nslog.js"></script>
    <!-- jquery -->
    <script type="text/javascript" src="public/js/jquery2.1.4.js"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" href="public/lib/bootstrap/dist/css/bootstrap.css">
    <script type="text/javascript" src="public/lib/bootstrap/dist/js/bootstrap.js"></script>

    <!-- 这个标题 要留一个坑,给后代去实现自己的title -->
    <title>
        {{ block 'block_1_title'}}
            beyond心中の动漫神作
        {{ /block }}
    </title>

    <!-- 这个head中要 要留一个坑,给后代去链接自己的css或js-->
    {{ block 'block_2_head_css_js'}}
    {{ /block }}

    </head>  
    <body>
        <!-- include进来公共的顶部、底部、左侧边栏、右广告栏 -->
        {{ include './public_top.html' }}
        {{ include './public_left.html' }}
        
        <!-- 这个body最后要 要留一个坑,给后代去实现自己的content-->
        {{ block 'block_3_body_content'}}
                这是母板默认的正文<br/>
        {{ /block }}

        {{ include './public_right.html' }}
        {{ include './public_footer.html' }}

        <!-- 这个body最后要 要留一个坑,给后代去实现自己的js-->
        {{ block 'block_4_body_js'}}
        {{ /block }}
    </body>
</html>

第3步,现在就可以新建首页node_51_index.html了 ,

该首页 继承(extend) 自 base_layout.html母板

node_51_index.html完整代码如下:

<!-- 第0步,继承母板 -->
{{ extend './node_51_base_layout.html' }}

<!-- 第1步,自己的标题 -->
{{ block 'block_1_title'}}
	自己的标题_首页
{{ /block }}

<!-- 第2步,自己的head中的css或js -->
{{ block 'block_2_head_css_js'}}
	<style type="text/css">
		body{
			background-color: rgba(166,166,166,0.1);
		}
	</style>
	<script type="text/javascript">
		alert('我是index的head中的js')
	</script>
{{ /block }}

<!-- 第3步,自己的正文 -->
{{ block 'block_3_body_content'}}
	自己的正文<br/>
{{ /block }}

<!-- 第4步,自己的正文最后的js代码 -->
{{ block 'block_4_body_js'}}
	<script type="text/javascript">
		alert('我是index的body最后的js')
	</script>
{{ /block }}

入口文件node_51_index.js代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  


// 导入框架
var express = require('express')
var path = require('path')
// 创建服务器对象
var appServer = express()
// 监听端口,并启动服务
appServer.listen(5267,function (error) {
	if (error) {
		return NSLog('启动失败: ' + error)
	}
	NSLog('服务启动成功')
})
// -----------------------------------

// 静态资源请求时的 staticFileUrlPrefix
var staticFileUrlPrefix = '/public/'
// var staticFileUrlPrefix = '/public' 

// 访问也只能使用 localhost:5267/public/img/beyond.jpg
// 磁盘上的静态资源目录
var staticFilePath = './public/'   
// var staticFilePath = 'public' 

var callbackFunction = express.static(staticFilePath)
appServer.use(staticFileUrlPrefix,callbackFunction)
// 再开一个静态资源目录
appServer.use('/uploads/',express.static(path.join(__dirname,'uploads')))

// -----------------------------------

// 指明:对于 所有后缀为html 的模板文件 使用模板引擎
var templateFileSuffix = 'html'
appServer.engine(templateFileSuffix,require('express-art-template'))
// 下面这一句参数配置,可有可无
appServer.set('view options',{
	debug: process.env.NODE_ENV !== 'production'
})
// 注意:如果不想把模板文件放在默认的views目录下,则可以通过下面代码更改设置
// appServer.set('views','其他目录')

// -----------------------------------
// 使用middleware中间件body-parser进行post请求体中数据解析
var bodyParser = require('body-parser')
// 设置解析 application/x-www-form-urlencoded
appServer.use(bodyParser.urlencoded({extended: false}))        
// 设置解析 application/json
appServer.use(bodyParser.json())

// -----------------------------------
// 自定义路由设计的目的是:
// 1.让主入口程序的职责更加单一,代码更加简洁
//     1.1 创建服务
//     1.2 做一些服务相关的配置,比如:
//           1.2.1 静态资源配置
//           1.2.2 模板引擎配置
//           1.2.3 body-parse 解析表单
//           1.2.4 挂载自定义路由
//           1.2.5 监听端口,启动服务
// 使用自定义的路由模块 必须使用./
// 注意: 配置模板引擎和body-parser, 一定要在挂载路由之前
var beyondRouter = require('./node_51_router')
appServer.use(beyondRouter)

路由文件node_51_router.js代码如下:

function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};  
/*
	自定义路由模块的职责是:
		专门处理所有的路由
		根据不同的请求方式和路径,采取相应的处理方法
*/ 
// express 专门提供了路由的处理方法
var express = require('express')

// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------

// 1.使用express专门提供的路由器处理路由 
var router = express.Router()

// -----------------------------------
// 时间格式化
// var BeyondDateFormatFunction = require('./BeyondDateFormat')

// ----------------首页-------------------
router.get('/',function (request,response) {
	response.render('index/node_51_index.html')
})

// 3.在模块文件最后,导出router
module.exports = router

通过node_51_index.js入口 加载 node_51_router.js路由之后

启动服务器,渲染效果如下:

Blog项目开始

第1步. 项目初始化

首先npm init -y ,生成package.json

然后git init,然后手动新建.gitignore文件

第2步. npm 安装 mongoose和express和art-template和express-art-template

第3步. 项目目录

public目录下有img和css和js和lib目录

views目录下放着html模板, 分成了登录注册模块、文章模块

第4步. 路由设计

路由设计
请求路由 方法 GET参数 POST参数 是否需要登录权限(没用到) 备注
/ GET 首页index.html即文章列表
/register GET 注册页面register.html
/register POST email,password,username 处理注册的POST请求
/login GET 登录页面login.html
/login POST email,password 处理登录POST请求

首先处理的路由是 / , 渲染首页node_52_index.html

由于数据库内暂时还没有数据,所以首页只用几个假的数据先填充,以保证样式正常

首页的渲染效果如下:

首页node_52_index.html代码(暂未使用模板引擎渲染)如下:

<!-- 第0步,继承母板 -->
{{ extend './node_52_base_layout.html' }}

<!-- 第1步,自己的标题 -->
{{ block 'block_1_title'}}
	未闻花名_多用户博客系统_首页
{{ /block }}

<!-- 第2步,自己的head中的css或js -->
{{ block 'block_2_head_css_js'}}
	<style type="text/css">
		body{
			background-color: rgba(166,166,166,0.1);
		}
	</style>
	<script type="text/javascript">
		// alert('我是index的head中的js')
	</script>
{{ /block }}

<!-- ||||||||||||||||||||||||||||||||| -->
<!-- 第3步,自己的正文 -->
{{ block 'block_3_body_content'}}
	
	
<section class="container">
  <ul class="media-list">
    <li class="media">
      <div class="media-left">
        <a href="#">
            <img width="40" height="40" class="media-object" src="public/img/beyond.jpg" alt="...">
          </a>
      </div>
      <div class="media-body">
        <h4 class="media-heading"><a href="/topics/123">未闻花名</a></h4>
        <p>面码 发起了话题 • 1314 人关注 • 32 个回复 • 1992 次浏览 • 2006-06-07 22:20</p>
      </div>
    </li>
    <li class="media">
      <div class="media-left">
        <a href="#">
            <img width="40" height="40" class="media-object" src="public/img/beyond.jpg" alt="...">
          </a>
      </div>
      <div class="media-body">
        <h4 class="media-heading"><a href="/topics/123">龙与虎</a></h4>
        <p>逢坂大河 发起了话题 • 520 人关注 • 5 个回复 • 871 次浏览 • 2008-05-20 13:14</p>
      </div>
    </li>
    <li class="media">
      <div class="media-left">
        <a href="#">
            <img width="40" height="40" class="media-object" src="public/img/beyond.jpg" alt="...">
          </a>
      </div>
      <div class="media-body">
        <h4 class="media-heading"><a href="/topics/123">轻音少女</a></h4>
        <p>平泽唯 发起了话题 • 67 人关注 • 1 个回复 • 244 次浏览 • 2010-11-11 17:20</p>
      </div>
    </li>
    <li class="media">
      <div class="media-left">
        <a href="#">
            <img width="40" height="40" class="media-object" src="public/img/beyond.jpg" alt="...">
          </a>
      </div>
      <div class="media-body">
        <h4 class="media-heading"><a href="/topics/123">这个杀手不太冷</a></h4>
        <p>mathilda 发起了话题 • 5 人关注 • 0 个回复 • 133 次浏览 • 2011-06-18 10:36</p>
      </div>
    </li>
  </ul>
  <nav aria-label="Page navigation" style="text-align:center;">
    <ul class="pagination">
      <li>
        <a href="#" aria-label="Previous">
        <span aria-hidden="true">«</span>
      </a>
      </li>
      <li class="active"><a href="#">1</a></li>
      <li><a href="#">2</a></li>
      <li><a href="#">3</a></li>
      <li><a href="#">4</a></li>
      <li><a href="#">5</a></li>
      <li>
        <a href="#" aria-label="Next">
        <span aria-hidden="true">»</span>
      </a>
      </li>
    </ul>
  </nav>
</section>

{{ /block }}
<!-- ||||||||||||||||||||||||||||||||| -->



<!-- 第4步,自己的正文最后的js代码 -->
{{ block 'block_4_body_js'}}
	<script type="text/javascript">
		// alert('我是index的body最后的js')
	</script>
{{ /block }}

注意: 首页node_52_index.html 是 继承(extend)自 母板 node_52_base_layout.html

母板node_52_base_layout.html代码如下:

<!DOCTPYE html>  
<html lang="zh">  
<head>  
    <link rel="icon" href="public/img/beyond.jpg" type="image/x-icon"/>
    <meta charset="UTF-8">
    <meta name="author" content="beyond">
    <meta http-equiv="refresh" content="520">
    <meta name="description" content="未闻花名-免费零基础教程-beyond">
    <meta name="viewport" content="width=device-width, 
    initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" />
    <meta name="keywords" content="HTML,CSS,JAVASCRIPT,JQUERY,XML,JSON,C,C++,C#,OC,PHP,JAVA,JSP,PYTHON,RUBY,PERL,LUA,SQL,LINUX,SHELL,汇编,日语,英语,泰语,韩语,俄语,粤语,阿语,魔方,乐理,动漫,PR,PS,AI,AE">
    <!--[if lt IE 9]>
        <script src="//apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
        <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js">
        </script>
        <![endif]-->

    <!-- 公共的样式 -->
    <style type="text/css">
        body{
            font-size: 100%; 
            /*声明margin和padding是个好习惯*/  
            margin: 0;  
            padding: 0; 
            background-image: url("public/img/sakura4.png");  
            background-repeat: no-repeat;  
            background-position: center center;  
        }
    </style>
    <!-- 公共的样式 -->
    <link rel="stylesheet" type="text/css" href="public/css/beyondbasestylewhite5.css">
    <!-- 绿色按钮的css效果 -->
    <link rel="stylesheet" type="text/css" href="public/css/beyondbuttongreen.css">        

    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" href="public/lib/bootstrap/node_52_v337_bootstrap.css">
    <!-- 公共的JS特效 -->
    <script type="text/javascript" src="public/js/nslog.js"></script>
    

    <!-- 这个标题 要留一个坑,给后代去实现自己的title -->
    <title>
        {{ block 'block_1_title'}}
            beyond心中の动漫神作
        {{ /block }}
    </title>

    <!-- 这个head中要 要留一个坑,给后代去链接自己的css或js-->
    {{ block 'block_2_head_css_js'}}
    {{ /block }}

    </head>  
    <body>
        <!-- include进来公共的顶部、底部、左侧边栏、右广告栏 -->
        {{ include './node_52_public_top.html' }}
        
        
        <!-- 这个body最后要 要留一个坑,给后代去实现自己的content-->
        {{ block 'block_3_body_content'}}
                这是母板默认的正文<br/>
        {{ /block }}


        <!-- jquery -->
        <script type="text/javascript" src="public/js/jquery2.1.4.js"></script>
    
        <script type="text/javascript" src="public/lib/bootstrap/node_52_v337_bootstrap.js"></script>


        {{ include './node_52_public_footer.html' }}

        <!-- 这个body最后要 要留一个坑,给后代去实现自己的js-->
        {{ block 'block_4_body_js'}}
        {{ /block }}



    </body>
</html>

注意:

母板node_52_base_layout.html中用到的子模板node_52_public_top.html 以及

子模板node_52_public_footer.html代码分别如下:

子模板node_52_public_top.html代码如下: (一会儿写了登录注册后,再完善)

<nav class="navbar navbar-default">
  <div class="container">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="/">
        <img width="90px" src="public/img/vwhm2.png" alt="">
      </a>
    </div>
    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <!-- <ul class="nav navbar-nav">
          <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
          <li><a href="#">Link</a></li>
          <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
            <ul class="dropdown-menu">
              <li><a href="#">Action</a></li>
              <li><a href="#">Another action</a></li>
              <li><a href="#">Something else here</a></li>
              <li role="separator" class="divider"></li>
              <li><a href="#">Separated link</a></li>
              <li role="separator" class="divider"></li>
              <li><a href="#">One more separated link</a></li>
            </ul>
          </li>
        </ul> -->
      <form class="navbar-form navbar-left">
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Search">
        </div>
      </form>
      <ul class="nav navbar-nav navbar-right">
        {{ if user }}
        <a class="btn btn-default navbar-btn" href="/topics/new">发起</a>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img width="20" height="20" src="public/img/beyond.jpg" alt=""> <span class="caret"></span></a>
          <ul class="dropdown-menu">
            <li class="dropdown-current-user">
              当前登录用户: {{ 一会写 }}
            </li>
            <li role="separator" class="divider"></li>
            <li><a href="#">个人主页</a></li>
            <li><a href="/settings/profile">设置</a></li>
            <li><a href="/logout">退出</a></li>
          </ul>
        </li>
        {{ else }}
        <a class="btn btn-primary navbar-btn" href="/login">登录</a>
        <a class="btn btn-success navbar-btn" href="/register">注册</a>
        {{ /if }}
      </ul>
    </div>
    <!-- /.navbar-collapse -->
  </div>
  <!-- /.container-fluid -->
</nav>

子模板node_52_public_footer.html代码如下:

<footer id="copyright">
	<p style="font-size:14px;text-align:center;font-style:italic;">  
		Copyright © <a id="author">2018</a> Powered by <a id="author">beyond</a>  
	</p>        
</footer>

目前为止,首页,效果如下:

接下来,点击首页->右上角按钮->弹出下拉菜单->单击注册按钮

注册页面效果如下:

开始渲染node_52_register.html注册页面: (包括使用ajax发送异步请求(POST注册)):

代码如下:

<!-- 第0步,继承母板 -->
{{ extend './node_52_base_layout.html' }}

<!-- 第1步,自己的标题 -->
{{ block 'block_1_title'}}
	未闻花名_多用户博客系统_注册
{{ /block }}

<!-- 第2步,自己的head中的css或js -->
{{ block 'block_2_head_css_js'}}
	<link rel="stylesheet" type="text/css" href="public/css/login.css">
	<script type="text/javascript">
		// alert('我是index的head中的js')
	</script>
{{ /block }}

<!-- ||||||||||||||||||||||||||||||||| -->
<!-- 第3步,自己的正文 -->
{{ block 'block_3_body_content'}}
	
<div class="main">
    <div class="header">
      <a href="/">
        <img src="/public/img/vwhm2.png" alt="" style="border:1px solid rgba(191,191,191,0.2);">
      </a>
      <p></p>
    </div>
    <!-- 
      表单具有默认的提交行为,默认是同步的,同步表单提交,浏览器会锁死(转圈儿)等待服务端的响应结果。
      表单的同步提交之后,无论服务端响应的是什么,都会直接把响应的结果覆盖掉当前页面。

      注意: 同步提交返回的结果 是直接覆盖掉 当前页面的内容

      后来有人想到了异步提交的办法,来解决这个问题。
     -->
    <form id="id_form_register" method="post" action="/register">
      <div class="form-group">
        <label for="email">邮箱</label>
        <input type="email" class="form-control" id="email" name="email" placeholder="Email" autofocus>
      </div>
      <div class="form-group">
        <label for="username">昵称</label>
        <input type="text" class="form-control" id="username" name="username" placeholder="Username">
      </div>
      <div class="form-group">
        <label for="password">密码</label>
        <input type="password" class="form-control" id="password" name="password" placeholder="Password">
      </div>
      <button type="submit" class="btn btn-success btn-block">注册</button>
    </form>
    <div class="message">
      <p>已有账号? <a href="/login">点击登录</a>.</p>
    </div>
    <p></p>
  </div>

{{ /block }}
<!-- ||||||||||||||||||||||||||||||||| -->



<!-- 第4步,自己的正文最后的js代码 -->
{{ block 'block_4_body_js'}}
	<!-- 使用blueimp-md5加密 (算了,只在服务端md5加密了)-->
	<script type="text/javascript" src="public/js/md5.min.js"></script>
	<script type="text/javascript">
		// alert('我是index的body最后的js')
		$('#id_form_register').on('submit',function (event) {
			// 阻止默认的事件行为
			event.preventDefault()
			// 获取表单数据 (使用jQuery中表单对象的serialize方法)
			var formData = $(this).serialize()
			// 发送ajax请求之前应该要先做客户端表单验证
			$.ajax({
				url: '/register',
				type: 'post',
				data: formData,
				dataType: 'json',
				success: function (responseObj) {
					/*
					err为0, 注册成功,客户端重定向(因为服务器重定向对 异步请求 无效)
					err为1, email已被注册过
					err为2, username已被占用
					err为500, 服务器崩溃
					*/ 
					var err = responseObj.err
					switch(err){
						case 0:
							window.location.href = '/'
							break
						case 1:
							window.alert('email已注册')
							break
						case 2:
							window.alert('username已存在')
							break
						case 3:
							window.alert('email已注册或username已存在')
							break
						case 500:
							window.alert('服务器崩溃' + responseObj.msg)
							break
						default:
							window.alert('未知异常')

					}
				}
			})

		})
	</script>
{{ /block }}

接下来编写服务端的代码, 以便处理ajax提交post过来的注册请求

这儿我们使用mongodb + mongoose中间件

第1步, 打开命令行,执行mongod 启动mongodb数据库服务

第2步,新建node_52_userdao.js,  创建User数据模型Schema

(注意: 后面我们还会创建articledao.js用来操作文章入库的CRUD)

(注意: 后面我们还会创建commentdao.js用来操作评论入库的CRUD)

node_52_userdao.js代码如下:

function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};  

// ----------------初始化------------------- 
var mongoose = require('mongoose')
// 连接数据库 db2,默认端口是: 27017
mongoose.connect('mongodb://localhost/db2')
var Schema = mongoose.Schema
// 设计用户表结构 
var userSchema = new Schema({
	email: {
		type: String,
		required: true
	},
	username: {
		type: String,
		required: true
	},
	password: {
		type: String,
		required: true
	},
	pubTime: {
		type: String,
		required: true,
		/* 
			type: Date,
			default: Date.now
			默认值:  只写一个函数名,不加括号就不会立即调用,
			因为一旦加括号,就会立即调用,会立刻执行
		*/
		default: ''
	},
	usersex: {
		type: Number,
		required: false,
		default: 0
	},
	userage: {
		type: Number,
		// enum: [8,9,10,11,12,13,14,15,16],
		default: 13
	},
	userimg: {
		// 用户头像url
		type: String,
		required: false,
		default: 'beyond.jpg'
	},
	userdescription: {
		type: String,
		required: false,
		default: 'vwhm.net'
	},
	userstatus: {
		// 0无限制,1限制评论,2限制发布,3限制登录
		type: Number,
		enum: [0,1,2,3],
		default: 0
	}
})


// 核心代码: 直接导出 模型构造函数
// 参数1: User 首字母必须大写,且必须是单数; 
// 这样就能自动生成users表(集合)
module.exports = mongoose.model('User',userSchema)

第3步,安装和配置body-parser中间件,自动化处理表单请求

第4步,保存入库,并将操作结果的json数据 回写浏览器

这个保存之前,要先查询 email 或者 username是否已经存在,

使用到了mongoose官方文档 中的 or 语法,mongoosejs.com/docs/api.html#query_Query-or

也可以参照mongodb官方文档 中的 or 条件语法

在node_52_router.js中,处理post过来的register请求时,

先判断数据库中是否已经存在email和username

如果查询错误,那么给浏览器报错500,Server Error
如果email已注册,那么给浏览器报错err = 1
如果username已存在,那么给浏览器报错err = 2

如果使用or查询,为了少写一个接口,那么给浏览器报错err = 3,email已注册或username已存在

如果查询结果为null,那么执行注册save操作,

如果save结果出错,那么给浏览器报错500

如果save成功,那么给浏览器返回{“err”:0,”msg”:”注册成功”}

浏览器就会收到异步请求返回的结果,进行客户端跳转了

在这期间, 插句题外话,推荐了一个客户端软件 MongoBooster 对MongoDB数据库进行可视化管理

安装后, 通过url  localhost:27017 进行连接mongodb数据库

然后就可以选择db2数据库,执行查询语句了…

在这期间,还插了一句题外话,使用md5 中间件

在github上,通过node + md5关键字, 搜索到了blueimp/JavaScript-MD5

客户端直接引入,然后使用

<script src="public/js/md5.min.js"></script>

就可以使用了

var hash = md5("password");

服务端先 npm install blueimp-md5 ,然后使用:

var hash = md5("password");

最后,处理注册请求的node_52_router.js代码如下:

function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};  
/*
	自定义路由模块的职责是:
		专门处理所有的路由
		根据不同的请求方式和路径,采取相应的处理方法
*/ 
// express 专门提供了路由的处理方法
var express = require('express')
// blueimp-md5对password加密再存入数据库
var md5 = require('./md5')
// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------

// 1.使用express专门提供的路由器处理路由 
var router = express.Router()

// ----------------引入dao模块-------------------
// 先对dao初始化
var UserDaoFunction = require('./node_52_userdao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')

// ----------------首页-------------------
router.get('/',function (request,response) {
	response.render('index/node_52_index.html')
})

// ----------------注册页面-------------------
router.get('/register',function (request,response) {
	response.render('index/node_52_register.html')
})

// ----------------进行注册-------------------
router.post('/register',function (request,response) {
	/*
	先判断数据库中是否已经存在email和username
	如果查询错误,报错500,Server Error
	如果email已注册,报错err = 1
	如果username已存在,报错err = 2
	如果使用or查询,为了少写一个接口,报错err = 3,email已注册或username已存在
	*/ 
	// 直接获取body-parser中post过来的表单
	var bodyObj = request.body
	// 使用blueimp-md5进行加密 (可加两次)
	bodyObj.password = md5(md5(bodyObj.password))
	// 手动添加当前时间
	bodyObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd hh:mm:ss')

	UserDaoFunction.findOne({
		$or: [
				{
					email: bodyObj.email
				},
				{
					username: bodyObj.username
				}
		]
	})
	.then(function (data) {
		// then方法的参数1是: resolveCallback
		NSLog('data: ' + data)
		if (data === null) {
			// 来到这儿说明 可以进行真正的注册
			// new 一个对象, 然后执行save方法并生成一个promise,为了避免callback hell 我们把它return, 目的是使用链式的then方法
			var promise_2 = new UserDaoFunction(bodyObj).save()
			return promise_2
		}

		// 来到这儿,说明有data 则表示 已存在了
		response.status(200).json({
				err: 3,
				msg: 'email已注册或username已存在'
		})
		
	},function (error) {
		// then方法的参数2是: rejectCallback
		//  如果查询失败,则返回500错误
		NSLog('error: ' + error)
		// express 内部 提供了一个json方法,自动把对象转成json
		response.status(500).json({
			err: 500,
			msg: error
		})
	})
	.then(function (data) {
		// then方法的参数1是: promise_2的resolveCallback
		// 保存成功
		response.status(200).json({
			err: 0,
			msg: '注册成功'
		})
	},function (error) {
		// then方法的参数2是: promise_2的rejectCallback
		// 保存失败
		response.status(500).json({
			err: 500,
			msg: error
		})
	})


})

// 3.在模块文件最后,导出router
module.exports = router

注册效果运行如下:

打开MongoBooster,使用command + R刷新一下, 查看数据库如下:

再次强调了一下, 服务端重定向,对于浏览器的异步请求无效!!! 记住就ok

表单具有默认的提交行为,默认是同步的,表单同步提交,浏览器会锁死(转圈儿)等待服务端的响应结果。

表单的同步提交之后,无论服务端响应的是什么,都会直接把响应的结果覆盖掉当前页面。

因此,以前的开发时,还要实现数据回显,即在重新渲染页面时,把前面提交过来的数据 填充到表单元素中

注意: 同步提交返回的结果 是直接覆盖掉 当前页面的内容

后来有人想到了异步提交的办法,来解决这个问题。

如今github仍然使用的是表单同步提交, 就是因为这样由服务端统一渲染,比较安全,虽然服务器压力大一些

补充一下,github 是一个 ruby on rails项目

由于express默认是不支持cookie和session的,

因此, 下面 使用express-session中间件,来保存用户登录的状态

express-session 官方文档: npmjs.com/package/express-session

第1步, 安装

npm install express-session –save

第2步, 配置 (一定要在appServer.use(router)之前)

// ---------express-session中间件配置------------------
// express-session 步骤2 
appServer.use(session({
	// 密钥,为了安全起见
	secret: 'vwhm.net',
	// 
	resave: false,
	// true表示 尚未使用 就进行初始化一个sessionID
	saveUninitialized: true
}))

第3步, 使用request.session.user 即可 设置或读取 session

(注意:此状态下的session 不是持久化的, 只是内存里,重启服务器就没有了)

我们在UserDao保存注册用户的时候,就将save()方法返回的user对象,存入session内

request.session.user = userFromDB

第4步, 在渲染node_52_index.html时, 将session中的user对象渲染过去

如果session有user,那么 显示 用户名

如果session没有user,那么 显示 登录和注册

第5步, 注销的话,只要 置null 并 delete request.session.user即可

效果如下:

node_52_index.js代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

// --------导入框架---------------------------
var express = require('express')
var path = require('path')
// express-session 步骤1
var session = require('express-session')

// --------创建并启动服务器--------------------------
var appServer = express()
// 监听端口,并启动服务
appServer.listen(5267,function (error) {
	if (error) {
		return NSLog('启动失败: ' + error)
	}
	NSLog('服务启动成功')
})

// --------静态资源配置---------------------------
// 静态资源请求时的 staticFileUrlPrefix
var staticFileUrlPrefix = '/public/'
// var staticFileUrlPrefix = '/public' 

// 访问也只能使用 localhost:5267/public/img/beyond.jpg
// 磁盘上的静态资源目录(强烈推荐转成绝对路径)
// var staticFilePath = './public/'   
// var staticFilePath = 'public' 
var staticFilePath = path.join(__dirname,'public')

var callbackFunction = express.static(staticFilePath)
appServer.use(staticFileUrlPrefix,callbackFunction)
// 再开一个静态资源目录
appServer.use('/uploads/',express.static(path.join(__dirname,'uploads')))

// ---------art-template模板引擎配置-------------------

// 指明:对于 所有后缀为html 的模板文件 使用模板引擎
var templateFileSuffix = 'html'
appServer.engine(templateFileSuffix,require('express-art-template'))
// 下面这一句参数配置,可有可无
appServer.set('view options',{
	debug: process.env.NODE_ENV !== 'production'
})
// 注意:如果不想把模板文件放在默认的views目录下,则可以通过下面代码更改设置
// appServer.set('views','其他目录')

// ---------body-parser中间件配置---------------------
// 使用middleware中间件body-parser进行post请求体中数据解析
var bodyParser = require('body-parser')
// 设置解析 application/x-www-form-urlencoded
appServer.use(bodyParser.urlencoded({extended: false}))        
// 设置解析 application/json
appServer.use(bodyParser.json())

// ---------express-session中间件配置------------------
// express-session 步骤2 
// 该中间件 会为request增加一个session的成员,默认类型为对象
// 本例中,session是在内存中保存的,一旦服务器重启就不在了
// 在实际生产环境下,需要对session进行持久化
appServer.use(session({
	// 自定义密钥,为了安全起见
	secret: 'vwhm.net',
	// 
	resave: false,
	// true表示 无论用不用session 都进行初始化一个sessionID
	saveUninitialized: true
}))
// -----------------------------------
// 自定义路由设计的目的是:
// 1.让主入口程序的职责更加单一,代码更加简洁
//     1.1 创建服务
//     1.2 做一些服务相关的配置,比如:
//           1.2.1 静态资源配置
//           1.2.2 模板引擎配置
//           1.2.3 body-parse 解析表单
//           1.2.4 挂载自定义路由
//           1.2.5 监听端口,启动服务
// 使用自定义的路由模块 必须使用./
// 注意: 配置模板引擎和body-parser, 一定要在挂载路由之前
var beyondRouter = require('./node_52_router')
appServer.use(beyondRouter)

node_52_router.js代码如下:

function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};  
/*
	自定义路由模块的职责是:
		专门处理所有的路由
		根据不同的请求方式和路径,采取相应的处理方法
*/ 
// express 专门提供了路由的处理方法
var express = require('express')
// blueimp-md5对password加密再存入数据库
var md5 = require('./md5')
// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------

// 1.使用express专门提供的路由器处理路由 
var router = express.Router()

// ----------------引入dao模块-------------------
// 先对dao初始化
var UserDaoFunction = require('./node_52_userdao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')

// ----------------首页-------------------
router.get('/',function (request,response) {
	response.render('index/node_52_index.html',{
		/*
		在渲染index.html时, 将session中的user对象渲染过去
		如果session有user,那么 显示 用户名
		如果session没有user,那么 显示 登录和注册
		*/ 
		user: request.session.user
	})
})

// ----------------注册页面-------------------
router.get('/register',function (request,response) {
	response.render('index/node_52_register.html')
})

// ----------------进行注册-------------------
router.post('/register',function (request,response) {
	/*
	先判断数据库中是否已经存在email和username
	如果查询错误,报错500,Server Error
	如果email已注册,报错err = 1
	如果username已存在,报错err = 2
	如果使用or查询,为了少写一个接口,报错err = 3,email已注册或username已存在
	*/ 
	// 直接获取body-parser中post过来的表单
	var bodyObj = request.body
	// 使用blueimp-md5进行加密 (可加两次)
	bodyObj.password = md5(md5(bodyObj.password))
	// 手动添加当前时间
	bodyObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd hh:mm:ss')

	UserDaoFunction.findOne({
		$or: [
				{
					email: bodyObj.email
				},
				{
					username: bodyObj.username
				}
		]
	})
	.then(function (data) {
		// then方法的参数1是: resolveCallback
		NSLog('data: ' + data)
		if (data === null) {
			// 来到这儿说明 可以进行真正的注册
			// new 一个对象, 然后执行save方法并生成一个promise,为了避免callback hell 我们把它return, 目的是使用链式的then方法
			var promise_2 = new UserDaoFunction(bodyObj).save()
			return promise_2
		}

		// 来到这儿,说明有data 则表示 已存在了
		response.status(200).json({
				err: 3,
				msg: 'email已注册或username已存在'
		})
		
	},function (error) {
		// then方法的参数2是: rejectCallback
		//  如果查询失败,则返回500错误
		NSLog('error: ' + error)
		// express 内部 提供了一个json方法,自动把对象转成json
		response.status(500).json({
			err: 500,
			msg: error
		})
	})
	.then(function (userFromDB) {
		// then方法的参数1是: promise_2的resolveCallback
		// 保存成功的话, 记录到session中
		request.session.user = userFromDB

		response.status(200).json({
			err: 0,
			msg: '注册成功'
		})
	},function (error) {
		// then方法的参数2是: promise_2的rejectCallback
		// 保存失败
		response.status(500).json({
			err: 500,
			msg: error
		})
	})


})
// ----------------注销请求-------------------
router.get('/logout',function (request,response) {
	// 清除session 并 重定向到首页
	request.session.user = null
	delete request.session.user
	response.redirect('/')
})

// 3.在模块文件最后,导出router
module.exports = router

最后再把登录功能实现一下 (后面的上传头像、发布、评论、找回密码等等等以后再写)

node_52_login.html代码如下:

<!-- 第0步,继承母板 -->
{{ extend './node_52_base_layout.html' }}

<!-- 第1步,自己的标题 -->
{{ block 'block_1_title'}}
	未闻花名_多用户博客系统_注册
{{ /block }}

<!-- 第2步,自己的head中的css或js -->
{{ block 'block_2_head_css_js'}}
	<link rel="stylesheet" type="text/css" href="public/css/login.css">
	<script type="text/javascript">
		// alert('我是index的head中的js')
	</script>
{{ /block }}

<!-- ||||||||||||||||||||||||||||||||| -->
<!-- 第3步,自己的正文 -->
{{ block 'block_3_body_content'}}
	
<div class="main">
    <div class="header">
      <a href="/">
        <img src="/public/img/vwhm2.png" alt="" style="border:1px solid rgba(191,191,191,0.2);">
      </a>
      <h1>用户登录</h1>
    </div>
    <form id="id_form_login">
      <div class="form-group">
        <label for="">邮箱</label>
        <input type="email" class="form-control" id="" name="email" placeholder="Email" autofocus>
      </div>
      <div class="form-group">
        <label for="">密码</label>
        <a class="pull-right" href="">忘记密码?</a>
        <input type="password" class="form-control" id="" name="password" placeholder="Password">
      </div>
      <div class="checkbox">
        <label>
          <input type="checkbox">记住我
        </label>
      </div>
      <button type="submit" class="btn btn-success btn-block">登录</button>
    </form>
    <div class="message">
      <p>没有账号? <a href="/register">点击创建</a>.</p>
    </div>
  </div>

{{ /block }}
<!-- ||||||||||||||||||||||||||||||||| -->



<!-- 第4步,自己的正文最后的js代码 -->
{{ block 'block_4_body_js'}}
	<!-- 使用blueimp-md5加密 (算了,只在服务端md5加密了)-->
	<script type="text/javascript" src="public/js/md5.min.js"></script>
	<script type="text/javascript">
		// alert('我是index的body最后的js')
		$('#id_form_login').on('submit',function (event) {
			// 阻止默认的事件行为
			event.preventDefault()
			// 获取表单数据 (使用jQuery中表单对象的serialize方法)
			var formData = $(this).serialize()
			// 发送ajax请求之前应该要先做客户端表单验证
			$.ajax({
				url: '/login',
				type: 'post',
				data: formData,
				dataType: 'json',
				success: function (responseObj) {
					/*
					err为0, 登录成功,客户端重定向(因为服务器重定向对 异步请求 无效)
					err为1, 邮箱或密码错误
					err为500, 服务器崩溃
					*/ 
					var err = responseObj.err
					switch(err){
						case 0:
							window.location.href = '/'
							break
						case 1:
							window.alert('邮箱或密码错误')
							break
						case 500:
							window.alert('服务器崩溃' + responseObj.msg)
							break
						default:
							window.alert('未知异常')

					}
				}
			})

		})
	</script>
{{ /block }}

包含了登录处理请求的node_52_router.js完整代码如下:

function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};  
/*
	自定义路由模块的职责是:
		专门处理所有的路由
		根据不同的请求方式和路径,采取相应的处理方法
*/ 
// express 专门提供了路由的处理方法
var express = require('express')
// blueimp-md5对password加密再存入数据库
var md5 = require('./md5')
// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------

// 1.使用express专门提供的路由器处理路由 
var router = express.Router()

// ----------------引入dao模块-------------------
// 先对dao初始化
var UserDaoFunction = require('./node_52_userdao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')

// ----------------首页-------------------
router.get('/',function (request,response) {
	response.render('index/node_52_index.html',{
		/*
		在渲染index.html时, 将session中的user对象渲染过去
		如果session有user,那么 显示 用户名
		如果session没有user,那么 显示 登录和注册
		*/ 
		user: request.session.user
	})
})

// ----------------注册页面-------------------
router.get('/register',function (request,response) {
	response.render('index/node_52_register.html')
})

// ----------------进行注册-------------------
router.post('/register',function (request,response) {
	/*
	先判断数据库中是否已经存在email和username
	如果查询错误,报错500,Server Error
	如果email已注册,报错err = 1
	如果username已存在,报错err = 2
	如果使用or查询,为了少写一个接口,报错err = 3,email已注册或username已存在
	*/ 
	// 直接获取body-parser中post过来的表单
	var bodyObj = request.body
	// 使用blueimp-md5进行加密 (可加两次)
	bodyObj.password = md5(md5(bodyObj.password))
	// 手动添加当前时间
	bodyObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd hh:mm:ss')

	UserDaoFunction.findOne({
		$or: [
				{
					email: bodyObj.email
				},
				{
					username: bodyObj.username
				}
		]
	})
	.then(function (data) {
		// then方法的参数1是: resolveCallback
		NSLog('data: ' + data)
		if (data === null) {
			// 来到这儿说明 可以进行真正的注册
			// new 一个对象, 然后执行save方法并生成一个promise,为了避免callback hell 我们把它return, 目的是使用链式的then方法
			var promise_2 = new UserDaoFunction(bodyObj).save()
			return promise_2
		}

		// 来到这儿,说明有data 则表示 已存在了
		response.status(200).json({
				err: 3,
				msg: 'email已注册或username已存在'
		})
		
	},function (error) {
		// then方法的参数2是: rejectCallback
		//  如果查询失败,则返回500错误
		NSLog('error: ' + error)
		// express 内部 提供了一个json方法,自动把对象转成json
		response.status(500).json({
			err: 500,
			msg: error
		})
	})
	.then(function (userFromDB) {
		// then方法的参数1是: promise_2的resolveCallback
		// 保存成功的话, 记录到session中
		request.session.user = userFromDB

		response.status(200).json({
			err: 0,
			msg: '注册成功'
		})
	},function (error) {
		// then方法的参数2是: promise_2的rejectCallback
		// 保存失败
		response.status(500).json({
			err: 500,
			msg: error
		})
	})
})

// ----------------注销请求-------------------
router.get('/logout',function (request,response) {
	// 清除session 并 重定向到首页
	request.session.user = null
	delete request.session.user
	response.redirect('/')
})

// ----------------登录界面-------------------
router.get('/login',function (request,response) {
	response.render('index/node_52_login.html')
})

// ----------------进行登录-------------------
router.post('/login',function (request,response) {
	// 1. 获取表单,对密码二次加密
	// 直接获取body-parser中post过来的表单
	var bodyObj = request.body
	// 使用blueimp-md5进行加密 (可加两次)
	bodyObj.password = md5(md5(bodyObj.password))
	// 2. 使用UserDao查询 
	UserDaoFunction.findOne({
		email: bodyObj.email,
		password: bodyObj.password
	})
	.then(function (userFromDB) {
		// 如果 userFromDB 为 null,表示 登录失败
		if (userFromDB === null) {
			return response.status(200).json({
				err: 1,
				msg: '邮箱或密码错误'
			})
		}
		// 来到这儿说明登录成功,记录session
		request.session.user = userFromDB
		response.status(200).json({
			err: 0,
			msg: '登录成功'
		})
	},function (error) {
		// then方法的参数2是: rejectCallback
		//  如果查询失败,则返回500错误
		NSLog('error: ' + error)
		// express 内部 提供了一个json方法,自动把对象转成json
		response.status(500).json({
			err: 500,
			msg: error
		})
	})
})









// 3.在模块文件最后,导出router
module.exports = router

最终效果如下: ((后面的上传头像、使用xheditor发布、评论、找回密码等等等以后再写))

NodeJS七天课程学习笔记_第8天 中间件_全局错误处理

推荐了chrome插件: EditThisCookie

推荐了模拟各种post和get等请求的工具: PostMan

一张图说明中间件的原理

中间件: 实质上是一种包装方法

中间件分为几种:

1. 不关心任何请求路径的中间件,如use方法

appServer.use( function(request,response,next ){

NSLog(‘请求被拦截下来了’)

// 请求又被放行了

next()

}

)

意思是任何请求,都会进入这个中间件

任何请求都会被这个use方法拦截下来, 后面的中间件就无法再获取到该请求了

除非在use方法最后一行, 调用next() ,放行该请求

2. 只关心 以/public开头的中间件, 如 /public/img/beyond.jpg或者/public/js/jquery.js

像这样的只有是以/public开头的请求,才会被拦截下来(不关心是post还是get方式)

 

appServer.use(‘/public‘, function(request,response,next ){

NSLog(‘请求被拦截下来了’)

// 请求又被放行了

next()

}

)

 

 

3.  严格匹配 请求方式 与 请求路径的中间件, 如get 和 post等

像这样的只有是/login的Get请求,才会被拦截下来

 

appServer.get(‘/login‘, function(request,response,next ){

NSLog(‘请求被拦截下来了’)

// 请求一般被处理了,不会再放行了, 要放行也可以…

next()

}

)

4. 错误处理中间件,  集中处理错误, use的参数只有1个函数,  该匿名函数 必须是四个参数

 

appServer.use(function(error,request,response,next ){

console.error(error.stack)

// 请求一般被处理了,不会再放行了, 要放行也可以…

next()

}

)

node_53.js完整代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

// 演示express中的三种中间件
var express = require('express')
var appServer = express()  

/*
 第1种,不关心任何请求路径的中间件: 
 use有两个参数: 
 参数1: 函数(带3个参数)
 任何请求,都会被拦截下来
 除非 手动调用next()才会将请求放行
*/
appServer.use(function (request,response,next) {
	NSLog('第1种_1:' + request.url,false)
	next()
})
appServer.use(function (request,response,next) {
	NSLog('第1种_2:' + request.url,false)
	next()
})


/*
 第2种,不关心请求方式, 只关心请求路径是以xxx开头的中间件: use有两个参数: 
 参数1: 以xxx开头的路径
 参数2: 函数(带3个参数)
 任何以xxx开头的请求,都会被拦截下来
 除非 手动调用next()才会将请求放行
*/
appServer.use('/public',function (request,response,next) {
	// 如果此时浏览器输入: localhost/public/img/beyond.jpg
	// 注意这时的url打印出来 就不再包含/public前缀了
	// 而是只有后半部分: /img/beyond.jpg
	NSLog('第2种_以public开头:' + request.url,false)
})
/*
 第3种,路由级别的中间件
 既严格匹配请求方式(get/post/put/delete等), 又严格匹配请求路径的中间件
 use有两个参数: 
 参数1: 严格匹配的路径
 参数2: 函数(带3个参数)
 任何精准匹配路径 并且 符合请求方式 的请求,都会被拦截下来
 除非 手动调用next()才会将请求放行
*/
appServer.get('/login',function (request,response,next) {
	NSLog('第3种_GET_Login:' + request.url,false)
})
appServer.post('/login',function (request,response,next) {
	NSLog('第3种_POST_Login:' + request.url,false)
})

// 最后,如果,没有能匹配的中间件,则Express 默认会提示Cannot GET /xxx
/*
   第4种, 错误处理中间件
   use参数只有1个 匿名函数
   该匿名函数的参数 必须是 4个
*/
appServer.use(function (error,request,response,next) {
	// 理论上是这样设置的, 但为啥不生效???
	NSLog('第4种_error:' + request.url,false)
	console.error(error.stack)
	response.status(500),send('404 Not Found')
})





// 监听端口,启动服务  
// 访问地址:localhost:5267  
appServer.listen(5267,function (error) {  
    if (error) {  
        return NSLog('启动失败: ' + error,false)  
    }  
    NSLog("服务启动成功",false)  
})  



效果如下: (第4种,全局错误处理,不知道为啥 不生效???!!!)

附录: express中关于中间件的 大概介绍

Using middleware

Express is a routing and middleware web framework that has minimal functionality of its own: An Express application is essentially a series of middleware function calls.

Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

Middleware functions can perform the following tasks:

  • Execute any code.
  • Make changes to the request and the response objects.
  • End the request-response cycle.
  • Call the next middleware function in the stack.

If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.

An Express application can use the following types of middleware:

You can load application-level and router-level middleware with an optional mount path.

You can also load a series of middleware functions together, which creates a sub-stack of the middleware system at a mount point.

Error-handling middleware

Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors.

Define error-handling middleware functions in the same way as other middleware functions, except with four arguments instead of three, specifically with the signature (err, req, res, next)):

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

内置中间件:

http://expressjs.com/en/guide/using-middleware.html

1. express.static
作用: serves static assets such as HTML,CSS,JS
2. express.json
作用: parses incoming requests with JSON payloads
3. express.urlencoded
作用: parses incoming requests with URL-encoded payloads

第3方中间件:
expressjs.com/en/resources/middleware.html
1. body-parser
2. compression
3. cookie-parser
4. morgan
5. response-time
6. serve-static
7. session

Middleware module Description Replaces built-in function (Express 3)
body-parser Parse HTTP request body. See also: bodyco-body, and raw-body. express.bodyParser
compression Compress HTTP responses. express.compress
connect-rid Generate unique request ID. NA
cookie-parser Parse cookie header and populate req.cookies. See also cookies and keygrip. express.cookieParser
cookie-session Establish cookie-based sessions. express.cookieSession
cors Enable cross-origin resource sharing (CORS) with various options. NA
csurf Protect from CSRF exploits. express.csrf
errorhandler Development error-handling/debugging. express.errorHandler
method-override Override HTTP methods using header. express.methodOverride
morgan HTTP request logger. express.logger
multer Handle multi-part form data. express.bodyParser
response-time Record HTTP response time. express.responseTime
serve-favicon Serve a favicon. express.favicon
serve-index Serve directory listing for a given path. express.directory
serve-static Serve static files. express.static
session Establish server-based sessions (development only). express.session
timeout Set a timeout period for HTTP request processing. express.timeout
vhost Create virtual domains. express.vhost

未完待续,下一章节,つづく