溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)

發(fā)布時(shí)間:2021-07-21 10:25:28 來(lái)源:億速云 閱讀:118 作者:Leah 欄目:web開(kāi)發(fā)

這篇文章給大家介紹node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā),內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

1.express框架安裝

1)在node命令行模式下輸入以下命令

npm install -g express

該命令在全局環(huán)境下安裝express框架,在安裝完這一步之后,并不能直接使用express命令來(lái)生成express項(xiàng)目,需要再安裝一個(gè)express項(xiàng)目生成器,在express2.X的版本中是不需要的,express4.X版本之后將項(xiàng)目生成器和express本身分離了出來(lái),如果不安裝express-generator這個(gè)生成器就使用express命令來(lái)生成項(xiàng)目,會(huì)遇到報(bào)express不是內(nèi)部或外部命令這個(gè)錯(cuò)誤,這是需要注意的地方,nodejs web開(kāi)發(fā)指南原書(shū)中是沒(méi)有安裝express-generator這一步的。

2)安裝express-generator

npm install -g express-generator

3)生成一個(gè)項(xiàng)目

cd ..
mkdir microblog
cd microblog
express micorblog

這里隨意在硬盤(pán)某個(gè)目錄下創(chuàng)建一個(gè)microblog的文件夾,進(jìn)入該文件夾,然后使用express microblog命令創(chuàng)建了一個(gè)microblog的express項(xiàng)目。

生成結(jié)構(gòu)如下:

node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)

其中app.js是項(xiàng)目入口文件,package.json是npm 包管理文件,bin文件夾里面的www.js放一些全局配置項(xiàng)以及命令行配置等。public 文件夾是用來(lái)存放項(xiàng)目靜態(tài)文件目錄如js,css以及圖片,routes文件夾是用來(lái)存放路由監(jiān)聽(tīng)的代碼相關(guān)文件。views文件夾用來(lái)存放模板文件,這里需要注意的是express4.X使用jade作為項(xiàng)目的默認(rèn)模板引擎,而在原書(shū)中是使用ejs作為模板引擎的,所以這里默認(rèn)生成的是jade文件。無(wú)可否認(rèn)ejs是要簡(jiǎn)單些,但是原理都是一樣的,我們使用jade作為開(kāi)發(fā)的模板引擎。

4)啟動(dòng)項(xiàng)目并查看

cd microblog
npm install
npm start

進(jìn)入到microblog文件夾,安裝項(xiàng)目所需相關(guān)模塊(根據(jù)pacakge.json文件),然后啟動(dòng)項(xiàng)目,這時(shí)候打開(kāi)瀏覽器查看項(xiàng)目輸入地址localhost:3000,結(jié)果如下說(shuō)明一切正常,

node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)

到目前為止,我們已經(jīng)擁有了一個(gè)在瀏覽器中運(yùn)行的web項(xiàng)目雛形。下面進(jìn)行開(kāi)發(fā),原書(shū)中的微博項(xiàng)目的主要功能是用戶能夠注冊(cè)登錄,權(quán)限控制并讓用戶發(fā)布微博在用戶個(gè)人主頁(yè)和項(xiàng)目首頁(yè)分別顯示,這些功能完整版代碼會(huì)提供,由于篇幅原因,這里以用戶注冊(cè)登錄模塊來(lái)說(shuō)明如何進(jìn)行一個(gè)完整流程的web開(kāi)發(fā)。

2.頁(yè)面布局

依照web開(kāi)發(fā)流程,我們首先來(lái)構(gòu)建一個(gè)項(xiàng)目主頁(yè),項(xiàng)目主頁(yè)是由布局文件layout.jade和內(nèi)容文件index.jade組成,關(guān)于的jade的學(xué)習(xí),這里提供兩個(gè)地址,對(duì)于以前使用過(guò)類(lèi)似模板引擎如smarty,razor等的,可以看看文檔就能夠上手做了,基本原理都是大同小異。

打開(kāi)views文件,將layout.jade文件代碼改寫(xiě)如下:

doctype html
html
 head
  title= title
  link(rel='stylesheet', href='/stylesheets/style.css')
 body
   nav.header
     ul.list
       li.logo
         a(href='/') Microblog
       li
         a(href='/') 首頁(yè)
       li
         a(href='/login') 登錄
       li
         a(href='/reg') 注冊(cè)
    div.container
      block content
      hr
      footer.footer
        p
          a(href='http://myzhibie.coding.io') myzhibie
          | @2015

需要注意父級(jí)元素和子元素的換行之間縮進(jìn),jade是利用縮進(jìn)來(lái)區(qū)別代碼層級(jí)的。

首頁(yè)內(nèi)容文件index.jade

extends layout
block content
  main.main
    section.intro
      if message
        h4.indexmes #{message}
      //如果用戶登錄或者注冊(cè)成功并且沒(méi)有在登錄狀態(tài)下點(diǎn)擊注冊(cè)或者登錄
      if success&&user
        h2.welcome #{success},歡迎 #{user} 來(lái)到 Microblog
      else if !success&&user
        h2.welcome 歡迎 #{user} 來(lái)到 Microblog
      else
        h2.welcome 歡迎來(lái)到 Microblog
      h4.tech Microblog是一個(gè)基于Node.js,使用express4.12.1,jade1.9.2以及MongoDB搭建起來(lái)的微博系統(tǒng),是對(duì)Node.js開(kāi)發(fā)指南一書(shū)中教學(xué)項(xiàng)目的重構(gòu)。
      p.btnlist
        if user
          a.login(href='/logout') 退出
          a.userlink(href='/users/#{user}') 發(fā)表文章
        else
          a.login(href='/login') 登錄
          a.register(href='/reg') 立即注冊(cè)
    section.show
      each val in posts
        article.col
          h4.author #{val.user}說(shuō)
          p
            | #{val.post}

首頁(yè)內(nèi)容是繼承了模板文件layout.jade.原書(shū)中使用的bootstrap來(lái)構(gòu)建頁(yè)面的css布局和樣式,這里我自己手寫(xiě)了一個(gè)仿bootstrap風(fēng)格的布局樣式,沒(méi)有應(yīng)用bootstrap,style.css文件如下:

body {
 padding: 50px;
 font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
html,body,ul,p,hr,h4{
  margin:0;
  padding: 0;
}
a {
 color: #00B7FF;
}
.header{
  background:#337aB7;
  width: 100%;
  height: 60px;
  color: #fff;
  font-size: 22px;
  overflow: hidden;
}
.list{
  line-height: 60px;
}
.navigation{
  overflow: hidden;
}
.list li{
  list-style: none;
  float: left;
  display: inline-block;
  margin-left: 20px;
  margin-right: 20px;
}
.list li a{
  text-decoration: none;
  color: #fff;
}

.list li a:hover{

}
.list li:not(:first-child) a:hover{
  font-size: 26px;
  color: #F5F5F5;
}
.logo{
  font-size: 26px;
  font-weight: 700;
}
.container{
  min-height: 500px;
  text-align: center;
  width: 100%;
}
.footer{
  width: 100%;
  height: 50px;
  font-size: 22px;
  background:#F5F5F5 ;
  line-height: 50px;
}
.footer a{
  color:#337aB7;
  text-decoration: none;
}
.main{
  color: #000000;
  width: 96%;
  margin: 30px auto;
}
.intro{
  width: 100%;
  margin:0 auto;
  border-radius: 5px;
  height: 300px;
  background:#F5F5F5 ;

}
.userintro{
  width: 100%;
  margin:0 auto;
  border-radius: 5px;
  height: 200px;
  background:#F5F5F5 ;
}
.welcome{
  padding-top: 50px;
  padding-left:50px;
  font-size: 50px;
  text-align: left;
  padding-bottom: 0;
  margin: 0;
}
.tech{
  text-align: left;
  padding-left:50px;
  margin: 0;
}
.show{
  overflow: hidden;
  width: 100%;
}
.show li{
  text-align: left;
  font-size: 18px;
}
.col{
  display: inline-block;
  float: left;
  width: 32%;
  height: 100px;
  overflow: hidden;
  padding-right: 20px;
  text-align: left;
  text-overflow: ellipsis;
}
.author{
  margin-top: 10px;
  margin-bottom: 3px;
}
.btnlist{
  padding-left: 50px;
  text-align: left;
}
.login{
  display: inline-block;
  padding-left: 15px;
  padding-right: 15px;
  height: 38px;
  line-height: 40px;
  background: -webkit-gradient(linear, left top, left bottom, from(#0068A6), to(#337aB7));
  color: #fff;
  text-align: center;
  border-radius: 5px;
  font-size: 20px;
  font-weight: 600;
  border: 1px solid #ccc;
  text-decoration: none;
  margin-right: 10px;
}
.register{
  display: inline-block;
  padding-left: 15px;
  padding-right: 15px;
  height: 38px;
  line-height: 40px;
  background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#F5F5F5));
  color: #000;
  text-align: center;
  border-radius: 5px;
  font-size: 20px;
  font-weight: 600;
  border: 1px solid #ccc;
  text-decoration: none;
}
.field{
  margin-top: 20px;
  margin-left: 50px;
  text-align: left;
  margin-bottom: 20px;
  border:none;
  border-bottom: 1px solid #ccc;
}
.label{
  font-size: 18px;
  font-weight: 600;
  line-height: 100%;
  display: inline-block;
  width: 10%;
  vertical-align: middle;
  text-align: right;
  padding-right: 10px;
}
.regheader{
  text-align: left;
  font-size: 24px;
  font-weight: 600;
}
.regform{
  text-align: left;
  padding-left: 100px;
  margin-bottom: 20px;
}
.regform input[type='text'],input[type='password']{
  width: 200px;
  height: 20px;
}
.regform input[type='submit']{
  width: 120px;
  height: 30px;
  color: #fff;
  background:-webkit-gradient(linear, left top, left bottom, from(#0068A6), to(#337aB7));
  border-radius: 5px;
  font-size: 20px;
}

.item{
  margin:20px;
  width: 100%;
}
.mess{
  font-size: 18px;
  color: #E73C3C;
  background: #F2DEDE;
  border-radius: 5px;
  width: 300px;
  text-align: center;
  margin-left: 100px;
}
.indexmes{
  height: 30px;
  line-height: 30px;
  background: #F2DEDE;
  color: #E73C3C;
}
.article{
  width: 60%;
  height: 30px;
  border-radius: 3px;
  border: 1px solid #A3C732;
  margin-top: 5px;
  font-size: 20px;
}
.submit{
  height: 40px;
  vertical-align: middle;
  padding: 0;
  margin-top: -5px;
  margin-left: 5px;
  width: 80px;
  background: #A3c732;
  font-size: 20px;
  border: none;
  border-radius: 5px;
  color: #fff;
}
.submitform{
  margin-top: 25px;
  margin-left: -10px;
}
.userlink{
  display: inline-block;
  text-decoration: none;
  line-height: 38px;
  height: 38px;
  vertical-align: middle;
  padding: 0;
  margin-top: -8px;
  margin-left: 5px;
  width: 90px;
  text-align: center;
  background: #A3c732;
  font-size: 20px;
  font-weight: 600;
  border-radius: 5px;
  color: #fff;
  border: 1px solid #ccc;
}
.usertitle{
  text-align: left;
  padding-top: 5px;
  padding-bottom: 0;
  padding-left: 5px;
  margin-bottom: 8px;
}
.usersuccess{
  height: 30px;
  background: #DFF0D8;
  line-height: 30px;
  color: #3C7668;
}

這個(gè)css文件是項(xiàng)目中所有的css全部包含在這里,所以比較龐大。到目前為止,可以查看首頁(yè)效果如下:

node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)

首頁(yè)中的數(shù)據(jù)都是之前自己測(cè)試過(guò)程中加入的,這里主要為了查看首頁(yè)效果,可以忽略這些數(shù)據(jù)。

由于這里要演示用戶注冊(cè)登錄模塊,用戶注冊(cè)模塊的模板文件reg.jade如下:

extends layout
block content
  h4.field.regheader #{title}
  form.regform(method='post')
    p.mess #{message}
    div.item
      label.label(for='username') 用戶名
      input(type='text',placeholder='輸入注冊(cè)用戶名',id='username',name='username')
    div.item
      label.label(for='password') 用戶密碼
      input(type='password',placeholder='用戶密碼',id='password',name='password')
    div.item
      label.label(for='passwordconf') 重復(fù)密碼
      input(type='password',placeholder='重復(fù)密碼',id='passwordconf',name='passwordconf')
    div.item
      label.label
      input(type='submit' id='sub',name='sub' value='注冊(cè)')

用戶登陸模板login.jade如下:

extends layout
block content
  h4.field.regheader #{title}
  form.regform(method='post')
    p.mess #{message}
    div.item
      label.label(for='username') 用戶名
      input(type='text',placeholder='輸入登陸用戶名',id='username',name='username')
    div.item
      label.label(for='password') 用戶密碼
      input(type='password',placeholder='用戶密碼',id='password',name='password')
    div.item
      label.label
      input(type='submit' id='sub',name='sub' value='登陸')

最終用戶注冊(cè)效果如下:

node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)

用戶登錄模塊和這個(gè)效果相仿,就不查看了,少了一個(gè)重復(fù)密碼的input而已。

下面我們需要編寫(xiě)用戶注冊(cè)的邏輯,在編寫(xiě)用戶注冊(cè)邏輯的前,用戶數(shù)據(jù)需要持久化,所以首先要安裝MongoDB數(shù)據(jù)庫(kù)在自己的機(jī)器上.

MongoDB這種nosql類(lèi)型的數(shù)據(jù)庫(kù),非常適合用戶存儲(chǔ)JSON對(duì)象類(lèi)型的數(shù)據(jù),有了mongoDB,就可以免去數(shù)據(jù)庫(kù)表設(shè)計(jì)部分的工作,對(duì)比以前使用的mysql,sqlserver以及oracle還是非常方便的。關(guān)于mongoDB數(shù)據(jù)庫(kù)的熟悉和學(xué)習(xí),推薦其官網(wǎng),官網(wǎng)詳細(xì)介紹了該數(shù)據(jù)庫(kù)的一切。英文不好可以去中文社區(qū)。同時(shí)為了使用nodejs來(lái)操作mongoDB數(shù)據(jù)庫(kù),我們使用mongoose這個(gè)對(duì)象模型,它是將mongoDB中的一個(gè)集合映射為nodejs中的一個(gè)model,然后在該model上提供操作這個(gè)集合的一些方法,使用它就可以避免我們自己利用nodejs提供的原生操作mongoDB數(shù)據(jù)庫(kù)的語(yǔ)法去手寫(xiě)數(shù)據(jù)庫(kù)CURD的方法,大大見(jiàn)曬了工作量,提高了開(kāi)發(fā)效率。關(guān)于mongoose的學(xué)習(xí),推薦去其官網(wǎng),里面詳述了它的安裝,使用以及API調(diào)用情況。

解決了mongoDB安裝和操作問(wèn)題,我們來(lái)對(duì)數(shù)據(jù)庫(kù)操作的model類(lèi),首先在項(xiàng)目路徑下建立一個(gè)db.js文件,用來(lái)連接數(shù)據(jù)庫(kù)并對(duì)數(shù)據(jù)庫(kù)進(jìn)行全局配置,如下

db.js

var settings=require("./settings");
var mongoose=require('mongoose');
mongoose.connect("mongodb://"+settings.ip+"/"+settings.db);
var db=mongoose.connection;
module.exports={
  "dbCon":db,
  "mongoose":mongoose
};

這里首先加載了配置文件settings.js文件,為了數(shù)據(jù)庫(kù)便于靈活修改,我們將某些信息存儲(chǔ)在配置文件中。然后加在了之前安裝的mongoose模塊,然后調(diào)用該模塊的connect方法來(lái)連接我們配置的數(shù)據(jù)庫(kù),然后將連接以對(duì)象的形式返回供外部調(diào)用。

settings.js

module.exports={
  "ip":"localhost",
  "db":"microblog",
  "host":27071
};

MongoDB的默認(rèn)端口是27071,一般可以使用默認(rèn)端口即可,數(shù)據(jù)庫(kù)連接大時(shí)候可以不指定端口,數(shù)據(jù)庫(kù)名為microblog.

然后以db.js返回的數(shù)據(jù)庫(kù)連接對(duì)象為基礎(chǔ),我們?cè)陧?xiàng)目根目錄下創(chuàng)建一個(gè)models文件夾,用來(lái)存放數(shù)據(jù)模型。創(chuàng)建一個(gè)user.js映射我們數(shù)據(jù)庫(kù)中的user集合(可以理解為user表),代碼如下:

var mongoose=require('../db').mongoose;
var schema=new mongoose.Schema({
  name:'string',
  password:'string'
});
var User=mongoose.model('User',schema);
module.exports=User;

這里首先獲得db.js中定義的連接對(duì)象,并以該對(duì)象為基礎(chǔ)構(gòu)造一個(gè)Schema(架構(gòu)),mogoose操作數(shù)據(jù)庫(kù)是以架構(gòu)為基礎(chǔ)的,類(lèi)似于我們其他ORM模型中屬性和方法的定義。這里我們定義了一個(gè)架構(gòu),擁有兩個(gè)屬性,name和password,都是string類(lèi)型,對(duì)應(yīng)用戶的用戶名和密碼。然后利用該架構(gòu)去創(chuàng)建一個(gè)model,該model上定義了對(duì)數(shù)據(jù)集合的增刪改查等方法,不用我們自己再去定義和編寫(xiě)其他代碼。在原書(shū)中這一節(jié)是利用node.js操作MongoDB數(shù)據(jù)庫(kù)的原生API去定義了一個(gè)user對(duì)象,然后在user對(duì)象上自定義了一些CRUD的方法??梢钥闯?,直接使用Mongoose可以大大減少開(kāi)發(fā)量并且擁有更好的效率和性能。

到目前為止,我們已經(jīng)有了界面(view),數(shù)據(jù)模型(model),就差邏輯代碼(controller)沒(méi)有編寫(xiě)了。在編寫(xiě)邏輯代碼之前需要先說(shuō)下express框架的特點(diǎn)以及它的整體運(yùn)行方式。由于本人使用過(guò)一些類(lèi)似的如Asp.net mvc,Yii以及thinkphp等MVC框架,使用express之后最大的感覺(jué)是這個(gè)框架夠輕量級(jí),尤其是express4.X之后,它僅僅保留了靜態(tài)文件路徑映射模塊作為該框架本身的內(nèi)置模塊,其他的功能都以中間件的形式采用require(modulename)進(jìn)行引入,只有引入后才能夠使用該模塊提供的功能。

express的工作原理是客戶端發(fā)送一個(gè)request,express接到該請(qǐng)求,可以將它進(jìn)行處理之后傳遞給其他中間件進(jìn)行處理,最終處理完成之后,采用respond.end或者response.render進(jìn)行頁(yè)面渲染或響應(yīng),進(jìn)行頁(yè)面渲染的時(shí)候,采用參數(shù)傳遞頁(yè)面需要的數(shù)據(jù)給對(duì)應(yīng)模板引擎,模板引擎收到數(shù)據(jù)然后按照自己的語(yǔ)法進(jìn)行替換生成對(duì)應(yīng)的html,最終返回給瀏覽器進(jìn)行渲染。

在express中,最關(guān)鍵的部分就是路有機(jī)制,我們所有基于請(qǐng)求做出的響應(yīng)都是對(duì)該路由進(jìn)行監(jiān)聽(tīng)捕獲的結(jié)果。舉個(gè)例子,如果我們請(qǐng)求一個(gè)路徑為http://localhost:3000/user,那么必須在routes文件夾下面的路徑監(jiān)聽(tīng)(暫且叫做監(jiān)聽(tīng)吧)的js文件中編寫(xiě)對(duì)該請(qǐng)求的響應(yīng)代碼,諸如app.post('/user',function(...){...})之類(lèi)的代碼,如果不存在這樣的代碼,就會(huì)報(bào)一個(gè)404錯(cuò)誤,因?yàn)檎?qǐng)求沒(méi)有得到響應(yīng),express實(shí)例不知道怎么去響應(yīng)這個(gè)請(qǐng)求。以上就是express大致的原理和工作流程,對(duì)于它的學(xué)習(xí),推薦去express官網(wǎng)直接去看文檔,講的很詳細(xì)。

現(xiàn)在回到用戶注冊(cè)模塊,我們注冊(cè)用戶常見(jiàn)的做法是注冊(cè)成功之后就默認(rèn)用戶已經(jīng)登錄,直接跳轉(zhuǎn)到歡迎登陸界面。在這里我們需要將用戶數(shù)據(jù)在注冊(cè)成功之后保存在session中,express框架對(duì)于session的支持是通過(guò)中間件express-session來(lái)的,使用方式依然是在npm 下安裝,然后在項(xiàng)目主文件中使用require加載,最后調(diào)用其提供的API,為了使用session,必須先安裝cookie的支持,這里利用cookie-parser這個(gè)中間件來(lái)為express框架提供cookie支持,它的具體使用方式可以去上面提供的地址自行查看。對(duì)于session,我們常見(jiàn)框架的做法是在服務(wù)器端將其存放到文件當(dāng)中,由于這里我們有了MongoDB數(shù)據(jù)庫(kù),更理想的狀態(tài)是將它存在數(shù)據(jù)庫(kù)中,這樣可以更靈活去控制。使用connect-mongo中間件可以將session存儲(chǔ)到mongoDB中,具體使用方式可按地址查看。

上述概念明確之后,我們?cè)陧?xiàng)目根目錄下的app.js(項(xiàng)目入口文件)中加載我們需要的中間件模塊和自定義的模塊如下:

app.js模塊加載代碼:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = require('./routes/index');
var users = require('./routes/users');
var session = require("express-session");
var MongoStore=require('connect-mongo')(session);
var db = require('./db');
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
  secret:"myzhibie",
  store:new MongoStore({
  mongooseConnection:db.dbCon
  })
}));


 app.use('/', routes);
 app.use('/users', users);

上述代碼就是加載各個(gè)中間件模塊并采用app.use來(lái)load這個(gè)模塊,其中上述代碼的最后一指定了將session存儲(chǔ)在MongoDB數(shù)據(jù)庫(kù)中,secret屬性是對(duì)session的簽名,通常是一個(gè)字符串,這是必選項(xiàng),如果不寫(xiě),

是無(wú)法完成將session存儲(chǔ)進(jìn)入數(shù)據(jù)庫(kù)的,關(guān)于該功能的更詳細(xì)介紹請(qǐng)查看文檔,最后兩句app.use('/',routes)和app.use('/users',users)代表對(duì)于這兩個(gè)路由的訪問(wèn)處理代碼我們封裝在了routes和users模塊中,er這兩個(gè)模塊都在routes文件夾下面。

完成了模塊引入加載和一些基本的設(shè)置,現(xiàn)在來(lái)編寫(xiě)用戶注冊(cè)的邏輯代碼,上面說(shuō)到對(duì)于路徑/的訪問(wèn)處理在routes模塊中,這個(gè)模塊指的就是routes文件夾下面的index.js,部分代碼如下:

var express = require('express');
var crypto = require('crypto');
var router = express.Router();
var db=require('../db');
var User=require('../models/user');
var Post=require('../models/post');
/* GET home page. */
router.get('/', function(req, res, next) {
  Post.find({},function(err,posts){
    if(err){
      req.session.message=err.message;
      return res.redirect('/');
    }
    res.render('index',{
      posts:posts
    });
  });

});
//發(fā)表微博
router.post('/post',function(req, res, next){
  var currentUser=req.session.user;
  var post=new Post({
    user:currentUser.name,
    post:req.body.article,
    updated:getTime(new Date())
  });
  post.save(function(err){
    if(err){
      req.session.message=err.message;
      return res.redirect('/reg');
    }
    req.session.success="發(fā)表成功";
    res.redirect('/users/'+currentUser.name);
  });


});

function getTime(date){
  return date.getFullYear()+
  "-"+date.getMonth()+1+"-"+
  date.getDate()+" "+
  date.getHours()+":"+
  date.getMinutes();
}
router.get('/reg', isLogin);
//用戶進(jìn)入注冊(cè)頁(yè)面
router.get('/reg',function(req,res){
  res.render('reg',{title:"用戶注冊(cè)"});
});
router.post('/reg', isLogin);
//用戶點(diǎn)擊注冊(cè)按鈕
router.post('/reg',function(req,res){
  if(req.body['password']!= req.body['passwordconf']){
    req.session.error="兩次密碼不一致";
    return res.redirect('/reg');
  }
  var md5=crypto.createHash('md5');
  var password=md5.update(req.body.password).digest('base64');
  var newUser=new User({
    name:req.body['username'],
    password:password
  });
  User.findOne({name:newUser.name},function(err,user){
    if(user){
      err="用戶名已經(jīng)存在";
    }
    if(err){
      req.session.error=err;
      return res.redirect('/reg');
    }
    newUser.save(function(err){
      if(err){
        req.session.error=err.message;
        return res.redirect('/reg');
      }
      req.session.user=newUser;
      req.session.success="注冊(cè)成功";
      res.redirect('/');
    });
  });
});
router.get('/login',isLogin);
router.get('/login',function(req,res){
  res.render('login',{title:"用戶登陸"});
});
router.post('/login',isLogin);
router.post('/login',function(req,res){
  var md5=crypto.createHash('md5');
  var password=md5.update(req.body.password).digest('base64');
  User.findOne({name:req.body.username},function(err,user){
    if(!user){
      req.session.error="用戶不存在";
      return res.redirect('/login');
    }
    if(user.password!=password){
      req.session.error="密碼錯(cuò)誤";
      return res.redirect('/login');
    }
      req.session.user=user;
      req.session.success="登錄成功";
      res.redirect('/');
  });
});
router.get('/logout',function(req,res){
  req.session.user=null;
  res.redirect('/');
});
function isLogin(req,res,next){
  if(req.session.user){
    req.session.message="用戶已登錄";
    return res.redirect('/');
  }
  next();
}
module.exports = router;

上述代碼1-6行都是對(duì)外部模塊的引入,8-19行是對(duì)首頁(yè)路由/的處理代碼。117行將該模塊定義為router供外部調(diào)用。我們主要看54-83行,這些代碼就是用戶注冊(cè)的代碼,54行監(jiān)聽(tīng)來(lái)自用戶對(duì)于/reg路由的post請(qǐng)求,首先判斷兩次密碼是否一致,如果不一致在session中存儲(chǔ)一個(gè)錯(cuò)誤信息然后跳轉(zhuǎn)到到當(dāng)前頁(yè)面顯示錯(cuò)誤信息,該錯(cuò)誤信息供模板引擎顯示給用戶。如果兩次密碼一致首先對(duì)密碼進(jìn)行md5加密,使用的是nodejs提供的核心模塊crypto,并生成一個(gè)對(duì)象模型User,該對(duì)象模型是mongoose中提供的一個(gè)model的實(shí)例,mongoose在它上面定義了一些操作數(shù)據(jù)庫(kù)的方法。然后調(diào)用這個(gè)實(shí)例的findOne方法檢測(cè)該用戶是否已經(jīng)存在,如果存在就保存錯(cuò)誤信息到session并跳轉(zhuǎn)到當(dāng)前頁(yè)顯示錯(cuò)誤。如果不存在這樣一個(gè)用戶就使用save方法進(jìn)行用戶信息保存,注冊(cè)成功后將用戶信息保存在session中,并保存一個(gè)success的提示信息,然后跳轉(zhuǎn)到首頁(yè)。這里需要注意一個(gè)坑,以前做php或者.net的時(shí)候,我們通常都是先查詢數(shù)據(jù)庫(kù)等數(shù)據(jù)庫(kù)返回結(jié)果提示用戶是否存在之后再進(jìn)行用戶的save然后在跳轉(zhuǎn),這是一種同步方式,跳轉(zhuǎn)操作需要等待findOne操作返回結(jié)果之后才能進(jìn)行。而nodejs中采用異步IO,最后的跳轉(zhuǎn)操作需要放在findOne操作的回調(diào)函數(shù)中進(jìn)行,跳轉(zhuǎn)操作不必等待findone操作結(jié)束后執(zhí)行,兩者是異步的。如果將最后的redirect操作放在findOne操作外部而不是回調(diào)函數(shù)中,你會(huì)在控制臺(tái)上得到一個(gè)Can't set headers after they are sent的錯(cuò)誤,這是因?yàn)樵趂ineOne以及save操作之前已經(jīng)進(jìn)行行了跳轉(zhuǎn),response響應(yīng)已經(jīng)結(jié)束,不能夠重復(fù)響應(yīng)請(qǐng)求。

到目前為止,用戶注冊(cè)模塊基本上已經(jīng)差不多完成了,最后需要說(shuō)一下如何在頁(yè)面上顯示提示信息或者錯(cuò)誤信息,之前我們將提示信息或者錯(cuò)誤信息都保存在了session中,jade要顯示錯(cuò)誤信息,它是不能夠直接訪問(wèn)session的,在express2.X即原書(shū)中是利用req.flash API+動(dòng)態(tài)視圖助手來(lái)實(shí)現(xiàn)的,就是發(fā)生錯(cuò)誤的時(shí)候先將其利用req.flash方法存儲(chǔ)下來(lái),然后利用動(dòng)態(tài)視圖助手結(jié)合模板去渲染給用戶。express4.X廢棄了這種方式,我們可以利用req.flash 的原理來(lái)自己模擬一個(gè)這種機(jī)制,同時(shí)利用res.locals變量被保存起來(lái),模板在渲染的時(shí)候是能夠訪問(wèn)到服務(wù)端這個(gè)變量的。關(guān)于res.locals的更多介紹請(qǐng)查看文檔。

為了模擬這種req.flash機(jī)制,我們?cè)陧?xiàng)目入口文件app.js(項(xiàng)目根目錄下)添加一段代碼如下:

app.use(function(req,res,next){
//  res.locals.user=req.session.user;
  var err=req.session.error;
  var success=req.session.success;
  var user=req.session.user;
  var mess=req.session.message;
  delete req.session.success;
  delete req.session.error;
  delete req.session.message;
  if(err){
    res.locals.message="*"+err;
  }
  if(mess){
    res.locals.message="*"+mess;
  }
  if(success){
    res.locals.success=success;
  }
  if(user){
    res.locals.user=user.name;
  }
  next();
});

這段代碼的意思是用戶請(qǐng)求和響應(yīng)的時(shí)候,捕獲session中存儲(chǔ)的錯(cuò)誤信息和用戶提示,將其存儲(chǔ)在response.locals變量中,這樣模板就能夠獲取。對(duì)于錯(cuò)誤信息和提示,由于只使用一次,存儲(chǔ)后立即使用delete刪除,對(duì)于用戶信息,需要持久保存下來(lái),則不刪除。

這樣,就能夠顯示用戶提示或者錯(cuò)誤信息。

下面演示一下完整的用戶注冊(cè)流程以及錯(cuò)誤信息提示。

當(dāng)用戶名存在或密碼不一致時(shí),

node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)

當(dāng)注冊(cè)成功后跳轉(zhuǎn)到首頁(yè)并顯示用戶注冊(cè)成功

node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)

關(guān)于node.js中怎么實(shí)現(xiàn)web開(kāi)發(fā)就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI