使用express搭建一個(gè)在線便利貼

Express

基于Node.js 平臺,快速浊伙、開放谤职、極簡的 web 開發(fā)框架。

  • Express使用文檔

  • 新建項(xiàng)目文件夾

      mkdir express-node
    
  • 初始化項(xiàng)目

      //因?yàn)槭莕ode項(xiàng)目齐莲,需要初始化一下痢站,生成package.json文件
      npm init -y  
      // -y 表示使用默認(rèn),和無腦回車相同选酗。
    
  • 安裝express

      npm install express --save
    
  • 切換鏡像源小工具

      npm install nrm    //解決安裝過慢問題
      nrm ls  //展示所有鏡像源
      例: nrm use taobao  //切換淘寶鏡像源
      // 如果自己發(fā)布包到npm阵难,需要再切換回來
    
  • Express 應(yīng)用生成器
    Express 應(yīng)用生成器-文檔

      npm install express-generator --save-dev   //不需要全局,有的機(jī)器會需要權(quán)限
    
     全局安裝了 express 運(yùn)行
     未全局安裝 ./node_modules/express-generator/bin/express-cli.js  運(yùn)行
    
     ./node_modules/express-generator/bin/express-cli.js ./ -f -e
     //在當(dāng)前目錄下強(qiáng)制創(chuàng)建ejs模板引擎  -h 查看所有模板引擎
     //會創(chuàng)建文件夾:bin芒填、public呜叫、routes、views
    
     提示安裝依賴
     install dependencies:  $ cd ./ && npm install
     npm install  //安裝依賴
    
  • 啟動服務(wù)器

      方法一: npm start  // 啟動服務(wù)器 ==>瀏覽器打開 http://localhost:3000/  
      方法二: node bin/www
    
  • 改變端口號

    // 因?yàn)閚pm start  才可以啟動服務(wù)器殿衰,
    在package.json文件中 start: "node ./bin/www"   
    在bin/www中 port = normalizePort(process.env.PORT || '3000');
    3000就是端口號
    或者啟動的時(shí)候 PORT=4000 node bin/www  啟動端口為4000
    80端口是默認(rèn)的端口朱庆,如果使用需要權(quán)限,需要再前面加 sudo
    window 需要權(quán)限闷祥,需使用管理員身份運(yùn)行
    
  • app.js

      app.set('views', path.join(__dirname, 'views'));
      // 把views設(shè)置為當(dāng)前模板路徑
      app.set('view engine', 'ejs');
      // 模板引擎 ejs
    
      // 中間鍵
      app.use(express.static(path.join(__dirname, 'public'))); 
      不走路由(routes文件夾)娱颊,直接從public中獲取
    
      //路由
      app.use('/', index);
      app.use('/users', users);
    
  • MVC
    控制路由跳轉(zhuǎn)的就是控制器
    視圖 views 的模板 就是V
    通過C去控制請求的流向,通過M去獲取數(shù)據(jù)凯砍,通過V去把數(shù)據(jù)渲染好箱硕,展示給用戶。

  • Express

  • 靜態(tài)頁面

      public文件夾中的靜態(tài)文件,
      app.use(express.static(path.join(__dirname, 'public')));
      static 設(shè)置靜態(tài)資源局待,從public中查找對應(yīng)文件夾斑响,根據(jù)文件夾目錄進(jìn)行查找
      如果找到了,就表示要的是文件钳榨,就把文件傳輸過來舰罚,不再往下查找
    
      例:注釋app.use(express.static(path.join(__dirname, 'public')));
      app.use('/stylesheets/style.css',function(req,res(){
        res.send('get style.css...')
      }))
      //就會走路由,把get style.css...傳到頁面薛耻。不注釋营罢,就會傳文件。
    

    換句話說:用戶請求的任何東西,對這個(gè)網(wǎng)站來說都是路由饲漾,static是專門對靜態(tài)資源進(jìn)行的攔截蝙搔,發(fā)現(xiàn)路徑和文件夾匹配上了,傳輸文件考传。所以吃型,后綴名沒有任何意義,只是給用戶分辨而已僚楞。

  • 新建src 文件夾
    目的勤晚,使用webpack編譯到public文件夾中,顯示給用戶

  • 搭建簡單測試環(huán)境


    糾正:webpack.config.js放在src下
     index.js
     var obj = require('../mod/b.js')
     console.log(obj)
    
     a.js
     module.exports.a='aaaaa'
    
     b.js
     var a = require('./a.js').a
     module.exports = {
       b: 'bbbb',
       a: a
     }
    
     webpack.config.js
     var webpack = require('webpack')
     var path = require('path')
     module.exports = {
         entry: path.join(__dirname,"js/app/index.js"),
         output:{
             path: path.join(__dirname,"../public/js"),
             filename: "index.js"
         }
     }
    
  • webpack壓縮

     安裝webpack
     npm install webpack --save-dev
     在package.json的script中添加
     "webpack":"webpack --config=src/webpack.config.js"
     npm run webpack
     壓縮路徑public/js/index.js泉褐,檢查文件代碼是否壓縮成功
    
  • 自動上傳小工具

     每次執(zhí)行npm run webpack 太麻煩赐写,使用自動上傳小工具,每次更改自動上傳
     npm install --save-dev onchange
     在package.json的script中添加
     "watch": "onchange src/**/*.js src/**/*.less -- npm run webpack "
     npm run watch 運(yùn)行自動壓縮
    
  • 切換node.js版本小工具

     npm install -g n
     n 6.10.0  切換版本
    
  • 新建toast板塊膜赃,提示便利貼增刪改查狀態(tài)

    • src --> mod --> toast.js
     function toast(msg,time){
      this.msg = msg;
      this.dismissTime = time||1000,
      this.createToast();
      this.showToast();
    }
    toast.prototype = {
      createToast: function(){
          var tpl = '<div class="toast">'+this.msg+'</div>';
          this.$toast = $(tpl);
          $('body').append(this.$toast);
      },
      showToast:function(){
          var self = this;
          this.$toast.fadeIn(300,function(){
              setTimeout(function(){
                  self.$toast.fadeOut(300,function(){
                      self.$toast.remove();
                  });
              },self.dismissTime);
          })
      }
    }
    
    function Toast(msg,time){
        return new toast(msg,time);
    }
    
    Toast('hello')
    
    module.exports.Toast = Toast;
    
    • src --> less --> toast.js
     .toast{
      position: fixed;
      left: 50%;
      transform: translateX(-50%);
      bottom: 20px;
      color: #D15A39;
      background: #fff;
      padding: 5px 10px;
      border-radius: 3px;
      box-shadow: 0px 0px 3px 1px rgba(255,255,255,0.6);
      display: none;
    }
    
  • LESS
    我的博客
    LESS中文文檔

    新建text.html

  • 運(yùn)行toast

      刪除a.js b.js 
      index.js ===>
      var Toast = require('../mod/toast.js').Toast;
      Toast('hello world')
      在src ==> js ==> lib ==>添加jQuery
      在toast.js中添加
      var $ = require('../lib/jquery.min.js') 引用jQuery
    
      簡化引入jQuery代碼
      在webpack.config.js中添加
      resolve:{
          alias:{ //當(dāng)require引入模塊的時(shí)候挺邀,簡化路徑
              jquery: path.join(__dirname,"js/lib/jquery.min.js"),
              mod: path.join(__dirname,"js/mod"),
              less: path.join(__dirname,"less")
          }
      },
    
      toast.js代碼var $ = require('../lib/jquery.min.js') 
      改成 var $ = require('jquery') 
    
      在webpack.config.js中添加
      plugins: [ //所有頁面都可以使用jQuery
          new webpack.ProvidePlugin({ 
              $: 'jquery'
          }),
      ]
      toast.js代碼var $ = require('jquery') 刪除也可以使用jQuery了。
    
      在toast.js中添加require('less/toast.less'),使用less
      在webpack.config.js中添加
      module:{
          rules: [
              {  //當(dāng)require一個(gè)東西的時(shí)候财剖,會進(jìn)行檢測
                  test: /\.less$/,  //正則表達(dá)式悠夯,以less為后綴
                  use: ["style-loader","css-loader","less-loader"]
                  //使用這些loader,向前解析 less解析成css躺坟,放到頁面上
              }
          ]
      },
      npm install --save css-loader less-loader style-loader less 安裝
      使用less
    
  • 新建event.js(src ==> js ==> mod == event.js)

    // 設(shè)計(jì)模式:發(fā)布訂閱模式;用于組件之間進(jìn)行一個(gè)解耦
    
    var EventCenter = (function(){
        var event = {};
        function on(evt,handler){
            events[evt] = events[evt] || [];
    
            events[evt].push({
                handler: handler
            });
        }
    
        function fire(evt,args){
            if(!events[evt]){
                return
            }
            for(var i = 0;i>events[evt].length;i++){
                events[evt][i].handler(args);
            }
        }
    
        return {
            on: on,
            fire:fire
        }
      })();
    
  • 新建waterfall.js(src ==> js ==> mod == waterfall.js)

      //瀑布流布局
      var WaterFall = (function(){
      var $ct;
      var $items;
    
      function render($c){
          $ct = $c;
          $items = $ct.children();
    
          var nodeWidth = $items.outerWidth(true),
            colNum = parseInt($(window).width()/nodeWidth),
            colSumHeight = [];
    
          for(var i = 0 ;i<colNum;i++){
              colSumHeight.push(0);
          }
    
          $items.each(function(){
              var $cur = $(this);
    
              // colSumHeight = [100, 250, 80 , 200]
    
              var idx = 0,
                  minSumHeight = colSumHeight[0];
    
              for(var i = 0;i<colSumHeight.length;i++){
                  if(colSumHeight[i] < minSumHeight){
                      idx = i;
                      minSumHeight = colSumHeight[i];
                  }
              }
    
              $cur.css({
                  left: nodeWidth*idx,
                  top: minSumHeight
              });
              colSumHeight[idx] = $cur.outerWidth(true) + colSumHeight[idx];
          });
      }
    
      $(window).on('resize',function(){
          render($ct);
      })
    
      return{
          init: render
      }
    })()
    
    // WaterFall.init($('#content'))
    
    module.exports = WaterFall
    
  • 新建note.js (src ==> js ==> mod == note.js)

    require('less/note.less');
    //拿到樣式
    
    /*
    { id: ''; text: 'hello';}
    */
    var Toast = require('./toast.js').Toast;
    var Event = require('mod/event.js');
    
    function Note(opts){
      this.initOpts(opts);
      this.createNote();  //創(chuàng)建便利貼
      this.setStyle();  //設(shè)置樣式
      this.bindEvent();
    }
    Note.prototype = {
      colors: [
        ['#ea9b35','#efb04e'], // headColor, containerColor
        ['#dd598b','#e672a2'],
        ['#eee34b','#f2eb67'],
        ['#c24226','#d15a39'],
        ['#c1c341','#d0d25c'],
        ['#3f78c3','#5591d2']
      ],
    
      defaultOpts: {
        id: '',   //Note的 id
        $ct: $('#content').length>0?$('#content'):$('body'),  
        //默認(rèn)存放Note 的容器
        context: 'input here'  //Note 的內(nèi)容
      },
    
      initOpts: function (opts) { //默認(rèn)一個(gè)初始化乳蓄,設(shè)置id和參數(shù)
        this.opts = $.extend({}, this.defaultOpts, opts||{});
        if(this.opts.id){
           this.id = this.opts.id;
        }
      },
    
      createNote: function () {
        var tpl =  '<div class="note">'
                + '<div class="note-head"><span class="delete">&times;</span></div>'
                + '<div class="note-ct" contenteditable="true"></div>'
                +'</div>';
        // 創(chuàng)建html咪橙,放到網(wǎng)頁上
        // contenteditable  不是input,設(shè)置H5屬性虚倒,可以編輯
        this.$note = $(tpl);
        this.$note.find('.note-ct').html(this.opts.context);
        this.opts.$ct.append(this.$note);
        if(!this.id)  this.$note.css('bottom', '10px');  //新增放到右邊
      },
    
      setStyle: function () {
        var color = this.colors[Math.floor(Math.random()*6)];
        this.$note.find('.note-head').css('background-color', color[0]);
        this.$note.find('.note-ct').css('background-color', color[1]);
      },
    
      setLayout: function(){
        var self = this;
        if(self.clk){
          clearTimeout(self.clk);  
        }
        self.clk = setTimeout(function(){
          Event.fire('waterfall');  //發(fā)送一個(gè)瀑布流
        },100);
      },
    
      bindEvent: function () {
        var self = this,
            $note = this.$note,
            $noteHead = $note.find('.note-head'),
            $noteCt = $note.find('.note-ct'),
            $delete = $note.find('.delete');
    
        $delete.on('click', function(){
          self.delete();  //調(diào)用刪除
        })
    
      //contenteditable沒有 change 事件美侦,所有這里做了模擬通過判斷元素內(nèi)容變動,執(zhí)行 save
      $noteCt.on('focus', function() {
        if($noteCt.html()=='input here') $noteCt.html('');
        $noteCt.data('before', $noteCt.html());
      }).on('blur paste', function() {
        if( $noteCt.data('before') != $noteCt.html() ) {
          $noteCt.data('before',$noteCt.html());
          self.setLayout();
          if(self.id){
            self.edit($noteCt.html())
          }else{
            self.add($noteCt.html())
          }
        }
      });
    
      //設(shè)置筆記的移動
      $noteHead.on('mousedown', function(e){  // 當(dāng)鼠標(biāo)點(diǎn)下去的時(shí)候
        var evtX = e.pageX - $note.offset().left,   
        //evtX 計(jì)算事件的觸發(fā)點(diǎn)在 dialog內(nèi)部到 dialog 的左邊緣的距離
            evtY = e.pageY - $note.offset().top;
        $note.addClass('draggable').data('evtPos', {x:evtX, y:evtY}); 
        //把事件到 dialog 邊緣的距離保存下來
      }).on('mouseup', function(){  // 鼠標(biāo)移開的時(shí)候魂奥,把class刪除掉
         $note.removeClass('draggable').removeData('pos');
      });
    
      $('body').on('mousemove', function(e){
        $('.draggable').length && $('.draggable').offset({
          top: e.pageY-$('.draggable').data('evtPos').y,   
          // 當(dāng)用戶鼠標(biāo)移動時(shí),根據(jù)鼠標(biāo)的位置和前面保存的距離,計(jì)算 dialog 的絕對位置
          left: e.pageX-$('.draggable').data('evtPos').x
        });
      });
    },
    
    edit: function (msg) {  //當(dāng)需要編輯的時(shí)候
      var self = this;
      $.post('/api/notes/edit',{
          id: this.id,
          note: msg
        }).done(function(ret){
        if(ret.status === 0){
          Toast('update success');
        }else{
          Toast(ret.errorMsg);
        }
      })
    },
    
    add: function (msg){
      console.log('addd...');
      var self = this;
      $.post('/api/notes/add', {note: msg})
        .done(function(ret){
          if(ret.status === 0){
            Toast('add success');
          }else{
            self.$note.remove();
            Event.fire('waterfall')
            Toast(ret.errorMsg);
          }
        });
      //todo
    },
    
    delete: function(){
      var self = this;
      $.post('/api/notes/delete', {id: this.id})
        .done(function(ret){
          if(ret.status === 0){
            Toast('delete success');
            self.$note.remove();
            Event.fire('waterfall')
          }else{
            Toast(ret.errorMsg);
          }
      });
    
    }
    
    };
    
    module.exports.Note = Note;
    
  • 新建note-manager.js(src ==> js ==> mod == note-manager.js)

    // 獲取數(shù)據(jù)歌懒,添加數(shù)據(jù)
    var Toast = require('./toast.js').Toast;
    var Note = require('./note.js').Note;
    var Toast = require('./toast.js').Toast;
    var Event = require('mod/event.js');
    
    
    var NoteManager = (function(){
    
      function load() {
        $.get('/api/notes')
          .done(function(ret){
            if(ret.status == 0){
              $.each(ret.data, function(idx, article) {
                  new Note({
                    id: article.id,
                    context: article.text
                  });
              });
    
              Event.fire('waterfall');
            }else{
              Toast(ret.errorMsg);
            }
          })
          .fail(function(){
            Toast('網(wǎng)絡(luò)異常');
          });
    
    
      }
    
      function add(){
        new Note();
      }
    
      return {
        load: load,
        add: add   //獲取數(shù)據(jù)茶袒,和添加
      }
    
    })();
    
    module.exports.NoteManager = NoteManager
    
  • 修改index.js (src ==> js ==> app == index.js)

    清空測試代碼,添加以下
    require('less/index.less');
    
    var NoteManager = require('mod/note-manager.js').NoteManager;
    var Event = require('mod/event.js');
    var WaterFall = require('mod/waterfall.js');
    
    NoteManager.load();  //加載所有的數(shù)據(jù)
    
    $('.add-note').on('click', function() {
      // 點(diǎn)擊添加按鈕哈蝇,調(diào)用添加
      NoteManager.add();
    })
    
    Event.on('waterfall', function(){
      //事件聽到waterfall的時(shí)候棺妓,執(zhí)行一次瀑布流
      WaterFall.init($('#content'));
    })
    
  • 添加index.less

  • 約定接口

      1.獲取所有的note: GET  /api/notes  req:{}  res:{ status: 0, data:[{},{}]}
      {status: 1,errorMsg:'失敗的原因'}
      2.創(chuàng)建一個(gè)note:POST: /api/notes/add req:{note: 'hello world'}  res:{ status:0}
      {status: 1,errorMsg:'失敗的原因'}
      3.修改一個(gè)note:POST:/api/notes/edit req:{note: 'new note',id:100}
      4.刪除一個(gè)note:POST:/api/notes/detele req:{id:100}
    
      app.js 添加 app.use('/api',api)
      routes中api.js添加以上接口
      router.get('/notes', function(req, res, next) {
        console.log('/notes')
      });
    
      router.post('/notes/add', function(req, res, next) {
        var note = req.body.note;
        console.log('add...')
      });
    
      router.post('/notes/edit', function(req, res, next) {
    
      });
    
      router.post('/notes/delete', function(req, res, next) {
    
      });
    
  • 數(shù)據(jù)庫
    SQL 教程
    npm mysql
    sequelize
    sequelize文檔 V3
    npm install --save sequelize

     npm install --save sqlite3
     如果node-v48-win32-x64.tar.gz卡住了,賦值url炮赦,fq下載怜跑,
     替換到node_modules/sqlite3/lib/binding 里面的 node-v48-linux-x64
    
      使用sequelize
      var Sequelize = require('sequelize');
      var sequelize = new Sequelize('database', 'username', 'password');
      // 連接數(shù)據(jù)庫
      
      //定義一個(gè)表,就是一個(gè)模型吠勘,對應(yīng)數(shù)據(jù)庫里一個(gè)表
      var User = sequelize.define('user', { // 對應(yīng)數(shù)據(jù)庫里性芬,user表
        username: Sequelize.STRING,   //string
        birthday: Sequelize.DATE    //date
      });
    
      sequelize.sync().then(function() {  //創(chuàng)建這個(gè)表
        return User.create({    //創(chuàng)建一個(gè)數(shù)據(jù)
          username: 'janedoe',
          birthday: new Date(1980, 6, 20)
        });
      }).then(function(jane) {
        console.log(jane.get({
          plain: true
        }));
      });
    
  • node端如何調(diào)試

      npm install -g node-inspector 安裝調(diào)試工具
      node-inspector  啟動調(diào)試軟件(默認(rèn)占用8080端口)  
      // 注意提示調(diào)試網(wǎng)址峡眶,通過該網(wǎng)址進(jìn)行調(diào)試
      還需要開啟服務(wù)器    node --debug bin/www
      刷新服務(wù)器網(wǎng)址,再刷新調(diào)試網(wǎng)站即可植锉。
    
  • 檢測數(shù)據(jù)能否使用

      在項(xiàng)目目錄下新建model ==> note.js
      var sequelize = new Sequelize(undefined, undefined, undefined, {  
        //不需要用戶名密碼辫樱,如果是其他數(shù)據(jù)庫需要做義工配置
        host: 'localhost',
        dialect: 'sqlite',
        storage: '../database/database.sqlite'
      });
      //測試是否成功,使用完成就可注釋掉
      sequelize
      .authenticate()
      .then(function(err){
        console.log('Connection has been established successfully.');
      })
      .catch(function(err){
        console.log('Unable to connect to the database:',err);
      })
       
      routes ==》 aip.js
      var Note = require('../model/note')
      router.get('/notes', function(req, res, next) {
        var data = Note.getAll()  //模型汽煮,用于具體的和數(shù)據(jù)進(jìn)行操作搏熄。
        //假設(shè)有個(gè)對象叫Note,有g(shù)etAll方法暇赤,把他所有數(shù)據(jù)賦值給data
        res.send({status: 0, data: notes})
      });
      cd model
      node note.js  會在database文件夾下生成空的database.sqlite
    
  • 向數(shù)據(jù)庫添加數(shù)據(jù)

      note.js添加數(shù)據(jù)心例,創(chuàng)建模型,一個(gè)模型鞋囊,對應(yīng)數(shù)據(jù)庫的一個(gè)表
      // id  [字段]  [添加時(shí)間] [更新時(shí)間]
      //模型起名叫note
      var Note = sequelize.define('note', {
        text: {
          type: Sequelize.STRING   
          //向數(shù)組添加字段止后,類型為string,會默認(rèn)添加id 添加時(shí)間 更新時(shí)間
        }
      });
    
    
      //sync({force: true}) 假設(shè)數(shù)據(jù)庫存在這個(gè)表溜腐,force:true刪除译株。
      Note.sync().then(function(){
         Note.create({text:'hello world'})
      }).then(function(){
        Note.findAll().then(function (notes) {   
        //findOne({raw: true}) 獲取里面的數(shù)據(jù)
          console.log(notes);
        })
      })
      //添加完成后注釋
      
      cd model  運(yùn)行 node note.js
      Note.findOne({raw: true, where:{id:2}}).then(function(notes){
        console.log(notes)
      })
      //findOne({raw: true}) 獲取里面的數(shù)據(jù),如果不加,會把所有數(shù)據(jù)展示
      // 會輸出 id 為 2 的數(shù)據(jù)挺益。
    
  • api.js中的數(shù)據(jù)增刪改查

      router.post('/notes/add', function(req, res, next) {
      if(!req.session || !req.session.user){
        return res.send({status: 1, errorMsg: '請先登錄'})
      }
      var uid = req.session.user.id
      var note = req.body.note;
    
      Note.create({text: note, uid: uid}).then(function(){
        res.send({status: 0})  //status: 0 成功
      }).catch(function(){
        res.send({status: 1,errorMsg: '數(shù)據(jù)庫出錯(cuò)'}) //status: 1 出錯(cuò)
      })
      console.log('add...',note)
    })
    
    router.post('/notes/edit', function(req, res, next) {
      if(!req.session || !req.session.user){
        return res.send({status: 1, errorMsg: '請先登錄'})
      }
      var uid = req.session.user.id
      Note.update({text: req.body.note},{where:{id:req.body.id,uid:uid}}).then(function(){
        console.log(arguments)
        res.send({status:0})
      }).catch(function(){
        res.send({status: 1,errorMsg: '數(shù)據(jù)庫出錯(cuò)'}) //status: 1 出錯(cuò)
      })
    });
    
    router.post('/notes/delete', function(req, res, next) {
      if(!req.session || !req.session.user){
        return res.send({status: 1, errorMsg: '請先登錄'})
      }
      var uid = req.session.user.id
      Note.destroy({where:{id:req.body.id,uid:uid}}).then(function(){
        res.send({status:0})
      }).catch(function(){
        res.send({status: 1,errorMsg: '數(shù)據(jù)庫出錯(cuò)'}) //status: 1 出錯(cuò)
      })
    });
    
  • 修改路徑

      使用更穩(wěn)定的路徑方式
      model ==》 note.js
      var path = require('path')  //添加到頁面
      storage: path.join(__dirname,'../database/database.sqlite') //修改頁面代碼
      module.exports.Note = Note;  //導(dǎo)出,提供給其外部使用
      
      routes ==> api.js
      var Note = require('../model/note').Note; //修改代碼歉糜,引用note.js
    
  • 登錄功能
    阮一峰 oauth 2.0文章
    oauth 2是一個(gè)認(rèn)證協(xié)議。當(dāng)點(diǎn)擊第三方登錄的時(shí)候望众,跳轉(zhuǎn)到第三方網(wǎng)站匪补,登錄成功后,返回當(dāng)前頁面烂翰。中間oauth2.0做一個(gè)支撐夯缺。(點(diǎn)擊登錄,向后臺發(fā)起請求甘耿,后臺向第三方接口請求踊兜,登錄成功,網(wǎng)站后臺拿到一個(gè)key佳恬,返回頁面捏境。)

    • app.js增加第三方登錄接口
      var auth = require('./routes/auth');
      app.use('/auth', auth);
    
    • 增加auth路由
      routes ==> 新建auth.js
    
      var passport = require('passport');
      var session = require('express-session');
    
      app.use(session({secret: '[隨便寫一串字符串典蝌,作為一個(gè)秘鑰]'}));
      app.use(passport.initialize());
      app.use(passport.session());
    
  • cookie 和 session的關(guān)系
    http是一個(gè)無狀態(tài)的協(xié)議,每次請求不知道是誰發(fā)的請求头谜,只知道有一個(gè)請求url過來骏掀,需要做什么事情。
    需求:用戶登錄了之后,刷新了頁面截驮,下次還是登錄狀態(tài)笑陈。需要記錄用戶的狀態(tài)。
    這個(gè)時(shí)候葵袭,需要使用cookie和session涵妥,當(dāng)使用用戶名和密碼提交給后臺的時(shí)候,后臺會從數(shù)據(jù)庫里面查詢坡锡,(一般密碼都不是明文蓬网,都是經(jīng)過MD5和SHA1加密的,會把密碼從新進(jìn)行一次加密鹉勒。)帆锋,加密好了之后,從數(shù)據(jù)庫查詢用戶名禽额,得到之后锯厢,再把加密后的密碼進(jìn)行匹配。如果匹配上了脯倒,就表示登錄上了实辑,表示登錄上了也沒用,所有需要記錄登錄狀態(tài)藻丢,把用戶的信息剪撬,再服務(wù)器里面創(chuàng)建一個(gè)session,一個(gè)session可以認(rèn)為是一個(gè)數(shù)據(jù)對象悠反,在內(nèi)存里面婿奔,session有一個(gè)key,key對應(yīng)的value就是session问慎,session可以存儲在任何地方,默認(rèn)存在內(nèi)存里挤茄,沒有把他存在某個(gè)地方如叼,是一個(gè)變量,存在服務(wù)器內(nèi)存穷劈,key就是一串很長的數(shù)值笼恰。
    用戶請求過來,向用戶展示頁面歇终,在服務(wù)器通過send cookie,相當(dāng)于http請求社证,當(dāng)用戶打開頁面的時(shí)候,發(fā)現(xiàn)http的請求里面评凝,有一個(gè)叫cookie,會把key存到cookie里面追葡,里面的文件connect.sid傳字符串。
    在刷新頁面,就會把所有請求都帶上宜肉,同時(shí)cookie也發(fā)到服務(wù)器里面匀钧,服務(wù)器就通過這個(gè)cookie,就從剛才的數(shù)據(jù)內(nèi)存里面去查找谬返,得到這個(gè)對象之斯,然后得到這個(gè)用戶,展示和這個(gè)用戶相關(guān)的信息遣铝。
    session是服務(wù)端的對象佑刷,有個(gè)key,把key發(fā)給瀏覽器酿炸,瀏覽器記錄下來瘫絮,這就是cookie。

  • 添加登錄注銷效果

    • index.ejs
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta property="qc:admins" content="4562636714562571563145" />
        <title><%= title %></title>
        <link rel="stylesheet" href="/css/index.css">
      </head>
      <body>
        <div id="header">
          <a class="add-note" title="添加筆記" href="#"><span class="fa fa-plus"></span> 添加</a>
          <ul class="user-area">
           <% if (isLogin){ %>
              <li><img src="<%= user.avatar %>" alt=""></li>
              <li><span title="<%= user.username %>"><%= user.username %></span></li>
              <li><span class="line"> | </span> </li>
              <li><a class="logout" href="/auth/logout">注銷</a></li>
            <%} else { %>
              <li><a class="login" title="GitHub登錄" href="/auth/github"> GitHub登錄</a>
              </li>
            <% } %>
          </ul>
        </div>
        <div id="content">
        </div>
        <div class="stars"></div>
        <script src="/js/index.js"></script>
    <!--     <div class="twinkling"></div> -->
      </body>
    </html>
    
    • index.js
    var express = require('express');
    var router = express.Router();
    /* GET home page. */
    router.get('/', function(req, res, next) {
      res.render('index', { title: '我的便利貼' });
    });
    router.get('/', function(req, res, next) {
      var data;
      if(req.session.user){
        data = {
          isLogin: true,
          user: req.session.user
        }
      }else{
        data = {
          isLogin: false
        }
      }
      console.log(data)
      res.render('index', data);
    });
    
    module.exports = router;
    
    • auth.js
    var express = require('express');
    var router = express.Router();
    
    var passport = require('passport');
    //引入passport梁沧,專門負(fù)責(zé)auth2的認(rèn)證檀何,所有的第三方登錄,都可以使用passport
    var GitHubStrategy = require('passport-github').Strategy;
    //在passport基礎(chǔ)上廷支,對整個(gè)協(xié)議進(jìn)行的封裝频鉴。
    
    passport.serializeUser(function(user, done) {
      console.log('---serializeUser---')
      console.log(user)
      done(null, user);
    });
    //把用戶登陸過來的信息,傳遞到passport之后恋拍,讓它生成一個(gè)session垛孔,存儲到內(nèi)存里面
    
    passport.deserializeUser(function(obj, done) {
      console.log('---deserializeUser---')
      done(null, obj);
    });
    // 用戶刷新頁面的時(shí)候,會再從內(nèi)存里面施敢,把對應(yīng)的session拿出來周荐,解析,知道這個(gè)用戶
    passport.use(new GitHubStrategy({
      clientID: '28928eae8774e53d2247',
      clientSecret: '0485fe61b10dac188ff17d977253ba091113f94d',
      callbackURL: "http://127.0.0.1:3000/auth/github/callback"
    },
    function(accessToken, refreshToken, profile, done) {
      // User.findOrCreate({ githubId: profile.id }, function (err, user) {
      // });
      done(null, profile);
    }
    ));
    //入口
    router.get('/github',
    passport.authenticate('github'));
    
    router.get('/github/callback',
    passport.authenticate('github', { failureRedirect: '/login' }),
    function(req, res) {
      req.session.user = {
        id: req.user.id,
        username: req.user.displayName || req.user.username,
        avatar: req.user._json.avatar_url,
        provider: req.user.provider
      };
      res.redirect('/');
    });
    
    //注銷
    router.get('/logout',function(req,res){
      req.session.destroy()  //銷毀session
      res.redirect('/')  //跳轉(zhuǎn)到首頁
    })
    
    module.exports = router;
    

npm start 開啟本地服務(wù)器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末僵娃,一起剝皮案震驚了整個(gè)濱河市概作,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌默怨,老刑警劉巖讯榕,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異匙睹,居然都是意外死亡愚屁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門痕檬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霎槐,“玉大人,你說我怎么就攤上這事梦谜∏鸬” “怎么了袭景?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碍岔。 經(jīng)常有香客問我浴讯,道長,這世上最難降的妖魔是什么蔼啦? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任榆纽,我火速辦了婚禮,結(jié)果婚禮上捏肢,老公的妹妹穿的比我還像新娘奈籽。我一直安慰自己,他們只是感情好鸵赫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布衣屏。 她就那樣靜靜地躺著,像睡著了一般辩棒。 火紅的嫁衣襯著肌膚如雪狼忱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天一睁,我揣著相機(jī)與錄音钻弄,去河邊找鬼。 笑死者吁,一個(gè)胖子當(dāng)著我的面吹牛窘俺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播复凳,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瘤泪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了育八?” 一聲冷哼從身側(cè)響起对途,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎髓棋,沒想到半個(gè)月后掀宋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仲锄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了湃鹊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片儒喊。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖币呵,靈堂內(nèi)的尸體忽然破棺而出怀愧,到底是詐尸還是另有隱情侨颈,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布芯义,位于F島的核電站哈垢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扛拨。R本人自食惡果不足惜耘分,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绑警。 院中可真熱鬧求泰,春花似錦、人聲如沸计盒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽北启。三九已至卜朗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咕村,已是汗流浹背场钉。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留培廓,地道東北人惹悄。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像肩钠,于是被迫代替她去往敵國和親泣港。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容