node.js(3)

1. MongoDB

MongoDB菜鳥教程

MongoDB中文網(wǎng)

MongoDB外網(wǎng)

mongoDB數(shù)據(jù)庫基本概念

  • MongDB是長得最像關(guān)系型數(shù)據(jù)庫的非關(guān)系型數(shù)據(jù)庫
    • 數(shù)據(jù)庫 --> 數(shù)據(jù)庫(可以有多個)
    • 數(shù)據(jù)表 --> 集合(數(shù)組)[表]
    • 表記錄 -->文檔對象

一個數(shù)據(jù)庫中可以有多個數(shù)據(jù)庫胁附,一個數(shù)據(jù)庫中可以有多個集合(數(shù)組),一個集合中可以有多個文檔(表記錄)

{
    qq:{
       user:[
           {name:"張三",age:17},{},{}...
       ]
    }弓候,
    taobao:{
       user:[
           {name:"張三",age:17},{},{}...
       ]
    },
}
  • 也就是說你可以任意的往里面存數(shù)據(jù)夸研,沒有結(jié)構(gòu)性這么一說

1.1 關(guān)系型和非關(guān)系型數(shù)據(jù)庫

1.1.1 關(guān)系型數(shù)據(jù)庫

(表就是關(guān)系亥至,或者說表與表之間存在關(guān)系)姐扮。

  • 所有的關(guān)系型數(shù)據(jù)庫都需要通過sql語言來操作
  • 所有的關(guān)系型數(shù)據(jù)庫在操作之前都需要設(shè)計表結(jié)構(gòu)
  • 而且數(shù)據(jù)表還支持約束
    • 唯一的
    • 主鍵
    • 默認(rèn)值
    • 非空

1.1.2 非關(guān)系型數(shù)據(jù)庫

  • 非關(guān)系型數(shù)據(jù)庫非常的靈活
  • 有的非關(guān)系型數(shù)據(jù)庫就是key-value對兒
  • 但MongDB是長得最像關(guān)系型數(shù)據(jù)庫的非關(guān)系型數(shù)據(jù)庫
    • 數(shù)據(jù)庫 --> 數(shù)據(jù)庫
    • 數(shù)據(jù)表 --> 集合(數(shù)組)
    • 表記錄 -->文檔對象

1.2 安裝

此電腦----右鍵屬性----高級系統(tǒng)設(shè)置----高級----環(huán)境變量----設(shè)置path:為bin目錄

  • 最后輸入mongod --version測試是否安裝成功

1.3 啟動和關(guān)閉數(shù)據(jù)庫

1.3.1 啟動:

  • mongodb 默認(rèn)使用執(zhí)行mongod 命令所處盤符根目錄下的/data/db作為自己的數(shù)據(jù)存儲目錄
  • 所以在第一次執(zhí)行該命令之前先自己手動新建一個 /data/db
mongod + 回車   => 啟動

如果想要修改默認(rèn)的數(shù)據(jù)存儲目錄,可以:

mongod --dbpath = 數(shù)據(jù)存儲目錄路徑

1.3.2 停止:

在開啟服務(wù)的控制臺睡榆,直接Ctrl+C;
或者直接關(guān)閉開啟服務(wù)的控制臺胀屿。

1.4 連接數(shù)據(jù)庫

連接:

# 該命令默認(rèn)連接本機(jī)的 MongoDB 服務(wù)
mongo

退出:

# 在連接狀態(tài)輸入 exit 退出連接
exit

1.5 基本命令

  • 查看數(shù)據(jù)庫列表(數(shù)據(jù)庫中的所有數(shù)據(jù)庫)
  show dbs
  • 查看當(dāng)前連接的數(shù)據(jù)庫
  db
  • 切換到指定的數(shù)據(jù)庫宿崭,(如果沒有會新建)
  use 數(shù)據(jù)庫名稱
  • 查看當(dāng)前目錄下的所有數(shù)據(jù)表
  show collections
  • 查看表中的詳細(xì)信息
  db.表名.find()  
  • 插入
db.students.insertOne({"name","Jack"})

1.6 Node操作MongoDB數(shù)據(jù)庫

  • 使用官方的MongoDB包來操作

http://mongodb.github.io/node-mongodb-native/

  • 使用第三方包mongoose來操作MongoDB數(shù)據(jù)庫)

2. mongoose

2.1 概述

(使用第三方包mongoose來操作MongoDB數(shù)據(jù)庫)

  • 第三方包:mongoose基于MongoDB官方的mongoDB包再一次做了封裝,名字叫mongoose讹堤,是WordPress項目團(tuán)隊開發(fā)的洲守。
  • 讓我們面對這樣的困境, 編寫MongoDB驗證梗醇,轉(zhuǎn)換和業(yè)務(wù)邏輯是非常麻煩的. 所以我們發(fā)明了Mongoose.
  • Mongoose為模型提供了一種直接的叙谨,基于scheme結(jié)構(gòu)去定義你的數(shù)據(jù)模型手负。它內(nèi)置數(shù)據(jù)驗證虫溜, 查詢構(gòu)建衡楞,業(yè)務(wù)邏輯鉤子等瘾境,開箱即用犬绒。

  • 安裝

  npm i mongoose

helloworld

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

const Cat = mongoose.model('Cat', { name: String });

const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));

2.2 官方指南

(設(shè)計Scheme 發(fā)布Model (創(chuàng)建表))

// 1.引包
// 注意:安裝后才能require使用
var mongoose = require('mongoose');

// 拿到schema圖表
var Schema = mongoose.Schema;

// 2.連接數(shù)據(jù)庫
// 指定連接數(shù)據(jù)庫后不需要存在凯力,當(dāng)你插入第一條數(shù)據(jù)庫后會自動創(chuàng)建數(shù)據(jù)庫
mongoose.connect('mongodb://localhost/test');  //27017

// 3.設(shè)計集合結(jié)構(gòu)(表結(jié)構(gòu))
// 用戶表
var userSchema = new Schema({
    username: { //姓名
        type: String,
        require: true //添加約束咐鹤,保證數(shù)據(jù)的完整性祈惶,讓數(shù)據(jù)按規(guī)矩統(tǒng)一
    },
    password: {
        type: String,
        require: true
    },
    email: {
        type: String
    }
});

// 4.將文檔結(jié)構(gòu)發(fā)布為模型
// mongoose.model方法就是用來將一個架構(gòu)發(fā)布為 model
//      第一個參數(shù):傳入一個大寫名詞單數(shù)字符串用來表示你的數(shù)據(jù)庫的名稱
//                  mongoose 會自動將大寫名詞的字符串生成 小寫復(fù)數(shù) 的集合名稱
//                  例如 這里會變成users集合名稱
//      第二個參數(shù):架構(gòu)
//  返回值:模型構(gòu)造函數(shù)
var User = mongoose.model('User', userSchema);

2.3 增刪改查

添加數(shù)據(jù)(增)

// 5.通過模型構(gòu)造函數(shù)對User中的數(shù)據(jù)進(jìn)行操作
var user = new User({
    username: 'admin',
    password: '123456',
    email: 'xiaochen@qq.com'
});

user.save(function(err, ret) {
    if (err) {
        console.log('保存失敗');
    } else {
        console.log('保存成功');
        console.log(ret);
    }
});

刪除(刪)

根據(jù)條件刪除所有:

User.remove({
    username: 'xiaoxiao'
}, function(err, ret) {
    if (err) {
        console.log('刪除失敗');
    } else {
        console.log('刪除成功');
        console.log(ret);
    }
});

根據(jù)條件刪除一個:

Model.findOneAndRemove(conditions,[options],[callback]);

根據(jù)id刪除一個:

User.findByIdAndRemove(id,[options],[callback]);

更新(改)

更新所有:

User.remove(conditions,doc,[options],[callback]);

根據(jù)指定條件更新一個:

User.FindOneAndUpdate([conditions],[update],[options],[callback]);

根據(jù)id更新一個:

// 更新   根據(jù)id來修改表數(shù)據(jù)
User.findByIdAndUpdate('5e6c5264fada77438c45dfcd', {
    username: 'junjun'
}, function(err, ret) {
    if (err) {
        console.log('更新失敗');
    } else {
        console.log('更新成功');
    }
});

查詢(查)

查詢所有:

// 查詢所有
User.find(function(err,ret){
    if(err){
        console.log('查詢失敗');
    }else{
        console.log(ret);
    }
});

條件查詢所有:

// 根據(jù)條件查詢
User.find({ username:'xiaoxiao' },function(err,ret){
    if(err){
        console.log('查詢失敗');
    }else{
        console.log(ret);
    }
});

條件查詢單個:

// 按照條件查詢單個(匹配的第一個)棒搜,查詢出來的數(shù)據(jù)是一個對象({})
// 沒有條件查詢使用findOne方法力麸,查詢的是表中的第一條數(shù)據(jù)
User.findOne({
    username: 'xiaoxiao'
}, function(err, ret) {
    if (err) {
        console.log('查詢失敗');
    } else {
        console.log(ret);
    }
});

3. CRUD案例重寫(MongoDB)

目錄結(jié)構(gòu)

[圖片上傳失敗...(image-fd4d21-1605691557285)]

code

Student.js

var mongoose = require('mongoose')

mongoose.connect('mongodb://localhost/itcast', { useMongoClient: true })

var Schema = mongoose.Schema

var studentSchema = new Schema({
  name: {
    type: String,
    required: true
  },
  gender: {
    type: Number,
    enum: [0, 1],
    default: 0
  },
  age: {
    type: Number
  },
  hobbies: {
    type: String
  }
})

// 直接導(dǎo)出模型構(gòu)造函數(shù)
module.exports = mongoose.model('Student', studentSchema)

Student-fs.js

/**
 * student.js
 * 數(shù)據(jù)操作文件模塊
 * 職責(zé):操作文件中的數(shù)據(jù),只處理數(shù)據(jù)座慰,不關(guān)心業(yè)務(wù)
 *
 * 這里才是我們學(xué)習(xí) Node 的精華部分:奧義之所在
 * 封裝異步 API
 */

var fs = require('fs')

var dbPath = './db.json'

/**
 * 獲取學(xué)生列表
 * @param  {Function} callback 回調(diào)函數(shù)
 */
exports.find = function (callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    callback(null, JSON.parse(data).students)
  })
}

/**
 * 根據(jù) id 獲取學(xué)生信息對象
 * @param  {Number}   id       學(xué)生 id
 * @param  {Function} callback 回調(diào)函數(shù)
 */
exports.findById = function (id, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students
    var ret = students.find(function (item) {
      return item.id === parseInt(id)
    })
    callback(null, ret)
  })
}

/**
 * 添加保存學(xué)生
 * @param  {Object}   student  學(xué)生對象
 * @param  {Function} callback 回調(diào)函數(shù)
 */
exports.save = function (student, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students

    // 添加 id 游盲,唯一不重復(fù)
    student.id = students[students.length - 1].id + 1

    // 把用戶傳遞的對象保存到數(shù)組中
    students.push(student)

    // 把對象數(shù)據(jù)轉(zhuǎn)換為字符串
    var fileData = JSON.stringify({
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
      if (err) {
        // 錯誤就是把錯誤對象傳遞給它
        return callback(err)
      }
      // 成功就沒錯益缎,所以錯誤對象是 null
      callback(null)
    })
  })
}

/**
 * 更新學(xué)生
 */
exports.updateById = function (student, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students

    // 注意:這里記得把 id 統(tǒng)一轉(zhuǎn)換為數(shù)字類型
    student.id = parseInt(student.id)

    // 你要修改誰莺奔,就需要把誰找出來
    // EcmaScript 6 中的一個數(shù)組方法:find
    // 需要接收一個函數(shù)作為參數(shù)
    // 當(dāng)某個遍歷項符合 item.id === student.id 條件的時候令哟,find 會終止遍歷屏富,同時返回遍歷項
    var stu = students.find(function (item) {
      return item.id === student.id
    })

    // 這種方式你就寫死了狠半,有 100 個難道就寫 100 次嗎神年?
    // stu.name = student.name
    // stu.age = student.age

    // 遍歷拷貝對象
    for (var key in student) {
      stu[key] = student[key]
    }

    // 把對象數(shù)據(jù)轉(zhuǎn)換為字符串
    var fileData = JSON.stringify({
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
      if (err) {
        // 錯誤就是把錯誤對象傳遞給它
        return callback(err)
      }
      // 成功就沒錯衣摩,所以錯誤對象是 null
      callback(null)
    })
  })
}

/**
 * 刪除學(xué)生
 */
exports.deleteById = function (id, callback) {
  fs.readFile(dbPath, 'utf8', function (err, data) {
    if (err) {
      return callback(err)
    }
    var students = JSON.parse(data).students

    // findIndex 方法專門用來根據(jù)條件查找元素的下標(biāo)
    var deleteId = students.findIndex(function (item) {
      return item.id === parseInt(id)
    })

    // 根據(jù)下標(biāo)從數(shù)組中刪除對應(yīng)的學(xué)生對象
    students.splice(deleteId, 1)

    // 把對象數(shù)據(jù)轉(zhuǎn)換為字符串
    var fileData = JSON.stringify({
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
      if (err) {
        // 錯誤就是把錯誤對象傳遞給它
        return callback(err)
      }
      // 成功就沒錯艾扮,所以錯誤對象是 null
      callback(null)
    })
  })
}

Router.js

var fs = require('fs')
var Student = require('./student')

// Express 提供了一種更好的方式
// 專門用來包裝路由的
var express = require('express')

// 1. 創(chuàng)建一個路由容器
var router = express.Router()

// 2. 把路由都掛載到 router 路由容器中

/*
 * 渲染學(xué)生列表頁面
 */
router.get('/students', function (req, res) {
  Student.find(function (err, students) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.render('index.html', {
      fruits: [
        '蘋果',
        '香蕉',
        '橘子'
      ],
      students: students
    })
  })
})

/*
 * 渲染添加學(xué)生頁面
 */
router.get('/students/new', function (req, res) {
  res.render('new.html')
})

/*
 * 處理添加學(xué)生
 */
router.post('/students/new', function (req, res) {
  // 1. 獲取表單數(shù)據(jù)
  // 2. 處理
  //    將數(shù)據(jù)保存到 db.json 文件中用以持久化
  // 3. 發(fā)送響應(yīng)
  new Student(req.body).save(function (err) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})

/*
 * 渲染編輯學(xué)生頁面
 */
router.get('/students/edit', function (req, res) {
  // 1. 在客戶端的列表頁中處理鏈接問題(需要有 id 參數(shù))
  // 2. 獲取要編輯的學(xué)生 id
  // 
  // 3. 渲染編輯頁面
  //    根據(jù) id 把學(xué)生信息查出來
  //    使用模板引擎渲染頁面

  // replace
  //    字符串模式
  //      簡單,但是不支持全局和忽略大小寫問題
  //    正則表達(dá)式模式
  //      強(qiáng)大酌予,支持全局和忽略大小寫
  Student.findById(req.query.id.replace(/"/g, ''), function (err, student) {
    if (err) {
      console.log(err)
      return res.status(500).send('Server error.')
    }
    res.render('edit.html', {
      student: student
    })
  })
})

/*
 * 處理編輯學(xué)生
 */
router.post('/students/edit', function (req, res) {
  // 1. 獲取表單數(shù)據(jù)
  //    req.body
  // 2. 更新
  //    Student.updateById()
  // 3. 發(fā)送響應(yīng)
  var id = req.body.id.replace(/"/g, '')
  Student.findByIdAndUpdate(id, req.body, function (err) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})

/*
 * 處理刪除學(xué)生
 */
router.get('/students/delete', function (req, res) {
  // 1. 獲取要刪除的 id
  // 2. 根據(jù) id 執(zhí)行刪除操作
  // 3. 根據(jù)操作結(jié)果發(fā)送響應(yīng)數(shù)據(jù)

  var id = req.query.id.replace(/"/g, '')
  Student.findByIdAndRemove(id, function (err) {
    if (err) {
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})

// 3. 把 router 導(dǎo)出
module.exports = router

4. 使用Node操作MySQL數(shù)據(jù)庫

文檔:https://www.npmjs.com/package/mysql

安裝:

npm install --save mysql

helloworld

// 引入mysql包
var mysql = require('mysql');
// 1. 創(chuàng)建連接
var connection = mysql.createConnection({
  host     : 'localhost',   //本機(jī)
  user     : 'me',      //賬號root
  password : 'secret',  //密碼12345
  database : 'my_db'    //數(shù)據(jù)庫名
});
 
// 2. 連接數(shù)據(jù)庫 (打開冰箱門)
connection.connect();
 
// 3. 執(zhí)行數(shù)據(jù)操作(把大象放到冰箱)
connection.query('SELECT * FROM `users` ', function (error, results, fields) {
  if (error) throw error;//拋出異常阻止代碼往下執(zhí)行
  // 沒有異常打印輸出結(jié)果
  console.log('The solution is: ',results);
});

// 4. 關(guān)閉連接  (關(guān)閉冰箱門)
connection.end();

code

var mysql = require('mysql');

// 1. 創(chuàng)建連接
var connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'root',
  database: 'users' // 對不起,我一不小心把數(shù)據(jù)庫名字和表名起成一樣的简僧,你知道就行
});

// 2. 連接數(shù)據(jù)庫 打開冰箱門
connection.connect();

// 3. 執(zhí)行數(shù)據(jù)操作 把大象放到冰箱
connection.query('SELECT * FROM `users`', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results);
});

// connection.query('INSERT INTO users VALUES(NULL, "admin", "123456")', function (error, results, fields) {
//   if (error) throw error;
//   console.log('The solution is: ', results);
// });

// 4. 關(guān)閉連接 關(guān)閉冰箱門
connection.end();

5. 異步編程

回調(diào)函數(shù)

  • 獲取異步函數(shù)作用域內(nèi)部數(shù)據(jù)

不成立的情況下:

function add(x,y){
    console.log(1);
    setTimeout(function(){
        console.log(2);
        var ret = x + y;
        return ret;
    },1000);
    console.log(3);
    //到這里執(zhí)行就結(jié)束了棉姐,不會i等到前面的定時器,所以直接返回了默認(rèn)值 undefined
}

console.log(add(2,2));
// 結(jié)果是 1 3 undefined 4

使用回調(diào)函數(shù)解決:

回調(diào)函數(shù):通過一個函數(shù)笛洛,獲取函數(shù)內(nèi)部的操作苛让。(根據(jù)輸入得到輸出結(jié)果)

var ret;
function add(x,y,callback){
    // callback就是回調(diào)函數(shù)
    // var x = 10;
    // var y = 20;
    // var callback = function(ret){console.log(ret);}
    console.log(1);
    setTimeout(function(){
        var ret = x + y;
        callback(ret);
    },1000);
    console.log(3);
}
add(10,20,function(ret){
    console.log(ret);
});

注意:

凡是需要得到一個函數(shù)內(nèi)部異步操作的結(jié)果(setTimeout,readFile,writeFile,ajax,readdir)

這種情況必須通過 回調(diào)函數(shù) (異步API都會伴隨著一個回調(diào)函數(shù))

封裝ajax方法:

基于原生XMLHttpRequest封裝get方法:

var oReq = new XMLHttpRequest();
// 當(dāng)請求加載成功要調(diào)用指定的函數(shù)
oReq.onload = function(){
    console.log(oReq.responseText);
}
oReq.open("GET", "請求路徑",true);
oReq.send();
//===============================================
function get(url,callback){
    var oReq = new XMLHttpRequest();
    // 當(dāng)請求加載成功要調(diào)用指定的函數(shù)
    oReq.onload = function(){
        //console.log(oReq.responseText);
        callback(oReq.responseText);
    }
    oReq.open("GET", url,true);
    oReq.send();
}
get('data.json',function(data){
    console.log(data);
});

Promise

callback hell(回調(diào)地獄):

文件的讀取無法判斷執(zhí)行順序(文件的執(zhí)行順序是依據(jù)文件的大小來決定的)(異步api無法保證文件的執(zhí)行順序)

var fs = require('fs');

fs.readFile('./data/a.text','utf8',function(err,data){
    if(err){
        // 1 讀取失敗直接打印輸出讀取失敗
        return console.log('讀取失敗');
        // 2 拋出異常
        //      阻止程序的執(zhí)行
        //      把錯誤信息打印到控制臺
        throw err;
    }
    console.log(data);
});

fs.readFile('./data/b.text','utf8',function(err,data){
    if(err){
        // 1 讀取失敗直接打印輸出讀取失敗
        return console.log('讀取失敗');
        // 2 拋出異常
        //      阻止程序的執(zhí)行
        //      把錯誤信息打印到控制臺
        throw err;
    }
    console.log(data);
});

通過回調(diào)嵌套的方式來保證順序:

var fs = require('fs');

fs.readFile('./data/a.text','utf8',function(err,data){
    if(err){
        // 1 讀取失敗直接打印輸出讀取失敗
        return console.log('讀取失敗');
        // 2 拋出異常
        //      阻止程序的執(zhí)行
        //      把錯誤信息打印到控制臺
        throw err;
    }
    console.log(data);
    fs.readFile('./data/b.text','utf8',function(err,data){
        if(err){
            // 1 讀取失敗直接打印輸出讀取失敗
            return console.log('讀取失敗');
            // 2 拋出異常
            //      阻止程序的執(zhí)行
            //      把錯誤信息打印到控制臺
            throw err;
        }
        console.log(data);
        fs.readFile('./data/a.text','utf8',function(err,data){
            if(err){
                // 1 讀取失敗直接打印輸出讀取失敗
                return console.log('讀取失敗');
                // 2 拋出異常
                //      阻止程序的執(zhí)行
                //      把錯誤信息打印到控制臺
                throw err;
            }
            console.log(data);
        });
    });
});

為了解決以上編碼方式帶來的問題(回調(diào)地獄嵌套)枫吧,所以在EcmaScript6新增了一個API:Promise九杂。

  • Promise:承諾例隆,保證
  • Promise本身不是異步的,但往往都是內(nèi)部封裝一個異步任務(wù)

基本語法:

// 在EcmaScript 6中新增了一個API Promise
// Promise 是一個構(gòu)造函數(shù)

var fs = require('fs');
// 1 創(chuàng)建Promise容器        resolve:解決   reject:失敗
var p1 = new Promise(function(resolve, reject) {
    fs.readFile('./a.text', 'utf8', function(err, data) {
        if (err) {
            // console.log(err);
            // 把容器的Pending狀態(tài)變?yōu)閞ejected
            reject(err);
        } else {
            // console.log(data);
            // 把容器的Pending狀態(tài)變?yōu)閞esolve
            resolve(1234);
        }
    });
});

// 當(dāng)p1成功了镰禾,然后就(then)做指定的操作
// then方法接收的function就是容器中的resolve函數(shù)
p1
    .then(function(data) {
        console.log(data);
    }, function(err) {
        console.log('讀取文件失敗了', err);
    });

鏈?zhǔn)窖h(huán):

封裝Promise的readFile

var fs = require('fs');

function pReadFile(filePath) {
    return new Promise(function(resolve, reject) {
        fs.readFile(filePath, 'utf8', function(err, data) {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}

const fs = required('fs')

//解決回調(diào)地獄問題,先讀取文件1坞古,再文件2,然后文件3织堂,不用嵌套到函數(shù)里易阳,
//可以return出去吃粒,返回新的Promise實例
getFileByPath('/files/1.txt')
.then(function(data){
    console.log(data)
    return getFileByPath('/files/2.txt')
})
.then(function(data){
    console.log(data)
    return getFileByPath('/files/3.txt')
})
.then(function(data){
    console.log(data)
})
.catch(function(err){//捕獲錯誤,輸出錯誤信息
    console.log(err.message)
})

mongoose所有的API都支持Promise:

// 查詢所有
User.find()
    .then(function(data){
        console.log(data)
    })

注冊:

User.findOne({username:'admin'},function(user){
    if(user){
        console.log('用戶已存在')
    } else {
        new User({
             username:'aaa',
             password:'123',
             email:'fffff'
        }).save(function(){
            console.log('注冊成功');
        })
    }
})
User.findOne({
    username:'admin'
})
    .then(function(user){
        if(user){
            // 用戶已經(jīng)存在不能注冊
            console.log('用戶已存在');
        }
        else{
            // 用戶不存在可以注冊
            return new User({
                username:'aaa',
                password:'123',
                email:'fffff'
            }).save();
        }
    })
    .then(funciton(ret){
        console.log('注冊成功');
    })

Generator

async函數(shù)

6. 模板引擎

子模板和模板的繼承(模板引擎高級語法)【include黑竞,extend很魂,block】

注意:

模板頁:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>模板頁</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"/>
    {{ block 'head' }}{{ /block }}
</head>
<body>
    <!-- 通過include導(dǎo)入公共部分 -->
    {{include './header.html'}}
    
    <!-- 留一個位置 讓別的內(nèi)容去填充 -->
    {{ block  'content' }}
        <h1>默認(rèn)內(nèi)容</h1>
    {{ /block }}
    
    <!-- 通過include導(dǎo)入公共部分 -->
    {{include './footer.html'}}
    
    <!-- 公共樣式 -->
    <script src="/node_modules/jquery/dist/jquery.js" ></script>
    <script src="/node_modules/bootstrap/dist/js/bootstrap.js" ></script>
    {{ block 'script' }}{{ /block }}
</body>
</html>

模板的繼承:

header頁面:

<div id="">
    <h1>公共的頭部</h1>
</div>

footer頁面:

<div id="">
    <h1>公共的底部</h1>
</div>

模板頁的使用:

<!-- 繼承(extend:延伸,擴(kuò)展)模板也layout.html -->
<!-- 把layout.html頁面的內(nèi)容都拿進(jìn)來作為index.html頁面的內(nèi)容 -->
{{extend './layout.html'}}

<!-- 向模板頁面填充新的數(shù)據(jù) -->
<!-- 填充后就會替換掉layout頁面content中的數(shù)據(jù) -->
<!-- style樣式方面的內(nèi)容 -->
{{ block 'head' }}
    <style type="text/css">
        body{
            background-color: skyblue;
        }
    </style>
{{ /block }}
{{ block 'content' }}
    <div id="">
        <h1>Index頁面的內(nèi)容</h1>
    </div>
{{ /block }}
<!-- js部分的內(nèi)容 -->
{{ block 'script' }}
    <script type="text/javascript">
        
    </script>
{{ /block }}

6. 簡易博客案例

目錄結(jié)構(gòu)

.
|——app.js   項目的入口文件
|——controllers
|——models   存儲使用mongoose設(shè)計的數(shù)據(jù)模型
|——node_modules 第三方包
|——package.json 包描述文件
|——package-lock.json    第三方包版本鎖定文件(npm5之后才有)
|——public   公共靜態(tài)資源
|——routes
|——views    存儲視圖目錄

模板頁

路由設(shè)計

路由 方法 get參數(shù) post參數(shù) 是否需要登錄 備注
/ get 渲染首頁
/register(登錄) get 渲染注冊頁面
/register post email,nickname,password 處理注冊請求
/login get 渲染登陸界面
/login post email,password 處理登錄請求
/loginout get 處理退出請求

模型設(shè)計

功能實現(xiàn)

步驟

  • 創(chuàng)建目錄結(jié)構(gòu)
  • 整合靜態(tài)也-模板頁
    • include
    • block
    • extend
  • 設(shè)計用戶登陸,退出窃植,注冊的路由
  • 用戶注冊
    • 先處理客戶端頁面的內(nèi)容(表單控件的name巷怜,收集表單數(shù)據(jù)延塑,發(fā)起請求)
    • 服務(wù)端
      • 獲取從客戶端收到的數(shù)據(jù)
      • 操作數(shù)據(jù)庫
        • 如果有錯,發(fā)送500告訴客戶端服務(wù)器錯了‘
        • 其他的根據(jù)業(yè)務(wù)發(fā)送不同的響應(yīng)數(shù)據(jù)
  • 登錄
  • 退出

7. Express 中間件

中間件的概念

參考文檔:http://expressjs.com/en/guide/using-middleware.html

中間件:把很復(fù)雜的事情分割成單個侥涵,然后依次有條理的執(zhí)行芜飘。就是一個中間處理環(huán)節(jié)燃箭,有輸入招狸,有輸出裙戏。

說的通俗易懂點兒厕诡,中間件就是一個(從請求到響應(yīng)調(diào)用的方法)方法。

把數(shù)據(jù)從請求到響應(yīng)分步驟來處理葛作,每一個步驟都是一個中間處理環(huán)節(jié)赂蠢。

var http = require('http');
var url = require('url');

var cookie = require('./expressPtoject/cookie');
var query = require('./expressPtoject/query');
var postBody = require('./expressPtoject/post-body');

var server = http.createServer(function(){
    // 解析請求地址中的get參數(shù)
    // var obj = url.parse(req.url,true);
    // req.query = obj.query;
    query(req,res); //中間件
    
    // 解析請求地址中的post參數(shù)
    req.body = {
        foo:'bar'
    }
});

if(req.url === 'xxx'){
    // 處理請求
    ...
}

server.listen(3000,function(){
    console.log('3000 runing...');
});

同一個請求對象所經(jīng)過的中間件都是同一個請求對象和響應(yīng)對象虱岂。

var express = require('express');
var app = express();
app.get('/abc',function(req,res,next){
    // 同一個請求的req和res是一樣的第岖,
    // 可以前面存儲下面調(diào)用
    console.log('/abc');
    // req.foo = 'bar';
    req.body = {
        name:'xiaoxiao',
        age:18
    }
    next();
});
app.get('/abc',function(req,res,next){
    // console.log(req.foo);
    console.log(req.body);
    console.log('/abc');
});
app.listen(3000, function() {
    console.log('app is running at port 3000.');
});

中間件的分類:

應(yīng)用程序級別的中間件

萬能匹配(不關(guān)心任何請求路徑和請求方法的中間件):

app.use(function(req,res,next){
    console.log('Time',Date.now());
    next();
});

關(guān)心請求路徑和請求方法的中間件:

app.use('/a',function(req,res,next){
    console.log('Time',Date.now());
    next();
});

路由級別的中間件

嚴(yán)格匹配請求路徑和請求方法的中間件

get:

app.get('/',function(req,res){
    res.send('get');
});

post:

app.post('/a',function(req,res){
    res.send('post');
});

put:

app.put('/user',function(req,res){
    res.send('put');
});

delete:

app.delete('/delete',function(req,res){
    res.send('delete');
});

var express = require('express');
var app = express();

// 中間件:處理請求蔑滓,本質(zhì)就是個函數(shù)
// 在express中烫饼,對中間件有幾種分類

// 1 不關(guān)心任何請求路徑和請求方法的中間件
// 也就是說任何請求都會進(jìn)入這個中間件
// 中間件本身是一個方法杠纵,該方法接收三個參數(shù)
// Request 請求對象
// Response 響應(yīng)對象
// next 下一個中間件
// // 全局匹配中間件
// app.use(function(req, res, next) {
//  console.log('1');
//  // 當(dāng)一個請求進(jìn)入中間件后
//  // 如果需要請求另外一個方法則需要使用next()方法
//  next();
//  // next是一個方法比藻,用來調(diào)用下一個中間件
//  // 注意:next()方法調(diào)用下一個方法的時候银亲,也會匹配(不是調(diào)用緊挨著的哪一個)
// });
// app.use(function(req, res, next) {
//  console.log('2');
// });

// // 2 關(guān)心請求路徑的中間件
// // 以/xxx開頭的中間件
// app.use('/a',function(req, res, next) {
//  console.log(req.url);
// });

// 3 嚴(yán)格匹配請求方法和請求路徑的中間件
app.get('/',function(){
    console.log('/');
});
app.post('/a',function(){
    console.log('/a');
});

app.listen(3000, function() {
    console.log('app is running at port 3000.');
});

錯誤處理中間件

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

配置使用404中間件:

app.use(function(req,res){
    res.render('404.html');
});

配置全局錯誤處理中間件:

app.get('/a', function(req, res, next) {
    fs.readFile('.a/bc', funtion() {
        if (err) {
            // 當(dāng)調(diào)用next()傳參后务蝠,則直接進(jìn)入到全局錯誤處理中間件方法中
            // 當(dāng)發(fā)生全局錯誤的時候馏段,我們可以調(diào)用next傳遞錯誤對象
            // 然后被全局錯誤處理中間件匹配到并進(jìn)行處理
            next(err);
        }
    })
});
//全局錯誤處理中間件
app.use(function(err,req,res,next){
    res.status(500).json({
        err_code:500,
        message:err.message
    });
});

內(nèi)置中間件

第三方中間件

參考文檔:http://expressjs.com/en/resources/middleware.html

  • body-parser
  • compression
  • cookie-parser
  • mogran
  • response-time
  • server-static
  • session
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末院喜,一起剝皮案震驚了整個濱河市喷舀,隨后出現(xiàn)的幾起案子硫麻,更是在濱河造成了極大的恐慌,老刑警劉巖甲棍,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奢赂,居然都是意外死亡膳灶,警方通過查閱死者的電腦和手機(jī)立由,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進(jìn)店門锐膜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來道盏,“玉大人,你說我怎么就攤上這事媒咳∩瑁” “怎么了坠敷?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵渐溶,是天一觀的道長弄抬。 經(jīng)常有香客問我,道長弛槐,這世上最難降的妖魔是什么依啰? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任速警,我火速辦了婚禮闷旧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匠襟。我一直安慰自己酸舍,他們只是感情好里初,可當(dāng)我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布璧亮。 她就那樣靜靜地躺著斥难,像睡著了一般。 火紅的嫁衣襯著肌膚如雪群扶。 梳的紋絲不亂的頭發(fā)上竞阐,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天骆莹,我揣著相機(jī)與錄音幕垦,去河邊找鬼。 笑死先改,一個胖子當(dāng)著我的面吹牛仇奶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播岛抄,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼夫椭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了猾漫?” 一聲冷哼從身側(cè)響起悯周,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤禽翼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锐墙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溪北,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡之拨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年蚀乔,在試婚紗的時候發(fā)現(xiàn)自己被綠了菲茬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片派撕。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡腥刹,死狀恐怖汉买,靈堂內(nèi)的尸體忽然破棺而出蛙粘,到底是詐尸還是另有隱情,我是刑警寧澤穴肘,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布评抚,位于F島的核電站伯复,受9級特大地震影響啸如,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜想暗,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一说莫、第九天 我趴在偏房一處隱蔽的房頂上張望唬滑。 院中可真熱鬧棺弊,春花似錦、人聲如沸稻艰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躯保。三九已至澎语,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尸变,已是汗流浹背召烂。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工奏夫, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留历筝,地道東北人。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓仔雷,卻偏偏與公主長得像舔示,于是被迫代替她去往敵國和親惕稻。 傳聞我的和親對象是個殘疾皇子俺祠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,446評論 2 359