黑馬Nodejs筆記04

path模塊

path.basename(path) 獲取一個路徑中的文件名(包含擴展名)

path.basename(path,[.ext]) 獲取一個路徑中的文件名(不包含擴展名)

path.dirname(path) 獲取一個路徑中的目錄部分

path.extname(path) 獲取一個路徑中的文件的擴展名

path.isAbsolute(path) 判斷一個路徑是否是絕對路徑

path.parse(path) 路徑解析,解析成一個對象,如將c:/a/b/index.html解析成{root:'c:/’,dir:’c:/a/b’,base:’index.js’,ext:’.html’,name:’index’}

path.join(‘c:/a’,‘b’,‘\c’) 作用:將多個個亂七八糟的字符拼接成一個正確的路徑 結果: c:\\a\\b\\c (字符類型的,兩個反斜槓表示一個反斜槓)

在windows裏面路徑 \ ,在Mac OS裏面路徑 /

dirname和filename

在每個模塊中,除了require,exports等模塊相關API之外,還有兩個特殊的成員

  • __dirname 動態獲取可以用來獲取當前文件模塊所屬的絕對路徑
  • __filename 動態獲取可以用來獲取當前文件的絕對路徑

我們爲什麼需要__dirname?

在c:/code/foo/目錄下有兩個文件

一個a.txt

一個index.js文件,代碼如下:

var fs = require('fs')
//	./a.txt 相對於執行node命令所處的終端路徑,這不是錯誤,Node就是這樣設計的,一般在開發命令行工具的時候,這個設計是必須有用的。就是說,文件操作路徑中,相對路徑設計的就是相對於執行 node 命令所處路徑
fs.readFile('./a.txt','utf8',function(err,data){
    if(err){
        throw err
    }
    console.log(data)
})

在c:/code/目錄下有一個文件app.js代碼如下所示

var fooIndex = require('./foo/index.js')

我們在c:/code/目錄下執行node app.js 會報出找不到該a.txt這個文件的錯誤,錯誤原因就是./a.txt 相對於執行node命令所處的終端路徑造成的。

所以爲了解決這個問題,很簡單,只需要把相對路徑變爲絕對路徑就可以了

var fs = require('fs')
//	./a.txt 相對於執行node命令所處的終端路徑,這不是錯誤,Node就是這樣設計的。就是說,文件操作路徑中,相對路徑設計的就是相對於執行 node 命令所處路徑
fs.readFile('c:/code/foo/a.txt','utf8',function(err,data){
    if(err){
        throw err
    }
    console.log(data)
})

但是,當我們把自己的項目複製給別人時,路徑問題依然會出錯

我們這裏可以使用__dirname或者__filename來幫我們解決這個問題。在拼接路徑過程中,爲了避免手動拼接帶來的一些低級錯誤,所以推薦多使用path.join()來輔助拼接。所以爲了儘量避免剛纔所描述這個問題,大家以後在文件操作中使用的相對路徑都統一轉換爲動態的絕對路徑

補充:模塊中的路徑標識和這裏的路徑沒關係,不受影響(相對於文件模塊)

art-template中的include-extend-block語法

我們在寫頁面時,往往很多頁面有公共的部分,我們不想重複的去寫這種公共的部分,此時art-template提供了相關方面的功能,如下。

我們在art-template模板繼承和子模板目錄下,完成我們的操作

假如所有網頁中,header和footer是所有網頁的共有部分。其內容如下:

header.html

<div>
  <h1>公共的頭部</h1>
</div>

footer.html

<div>
  <h1>公共的底部</h1>
</div>

我們先寫好父模板loyout,這個模板用於其餘網頁繼承使用

loyout.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
  {{ block 'head' }}{{ /block }}
</head>
<body>
  {{ include './header.html' }}
    
  <!-- 留一個坑,將要留給孩子去填坑 -->
  {{ block 'content' }} <!-- 這裏的contont是名字 -->
    <h1>默認內容</h1>
  {{ /block }}

  {{ 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>

{{block}}{{/block}}這個標籤用於繼承者填充部分,如果繼承者不填充,則可以使用默認內容

下面我們分別在index.html和list.html使用這個父模板

index.html

{{extend './layout.html'}}

{{ block 'head' }}
<style>
  body {
    background-color: skyblue;
  }
</style>
{{ /block }}

{{ block 'content' }}
<div>
  <h1>index 頁面填坑內容</h1>
</div>
{{ /block }}

{{ block 'script' }}
<script>
  window.alert('index 頁面自己的 js 腳本')
</script>
{{ /block }}

list.html

{{extend './layout.html'}}

{{ block 'content' }}
<div>
  <h1>列表頁自己的內容</h1>
</div>
{{ /block }}

Nodejs注意事項

在設計文檔模型的時候,如:

var UserSchema = new Schema({
    create_time:{
        type:Date,
        //注意:這裏不要寫Date.now(),因爲會即刻調用
        //這裏直接給了一個方法:Data.now
        //當你去new Model的時候,如果你沒有傳遞create_time,則mongoose就會調用default指定的Date.now方法,使用其返回值作爲默認值
        default:Date.now
    }
})
module.exports = mongoose.model('User',userSchema)

Express在res提供了一個方法json({name:‘fang’}),這個方法可以將響應的對象的轉換爲JSON格式,響應的就是一個JSON格式的字符串了。

MD5加密

安裝一個包

npm install blueimp-md5 --save

在nodejs環境下對密碼進行加密

var md5 = require('blueimp-md5')
var password = 123456789
var password = md5(md5(password)) //加密兩次

表單同步提交和異步提交

表單具有默認的提交行爲,默認是同步的,同步表單提交,瀏覽器會鎖死(轉圈兒)等待服務端的響應結果

表單的同步提交之後,無論服務端響應的是什麼,都會直接把響應的結果覆蓋掉當前頁面

(在沒有Ajax之前)後來有人想到了一種辦法,來解決這個問題:後臺重新渲染同一個頁面給瀏覽器顯示

後來出現了ajax異步提交,在不刷新頁面的情況下,獲取響應數據。

服務端重定向只針對同步請求才有效,異步請求無效,所以對於異步請求重定向,客戶端接收到響應後,自己進行重定向,代碼如下

window.location.hrerf = '/'

Node中Cookie和Session

1、Cookie

HTTP是無狀態協議,客戶端每次發出請求時候,下一次請求得不到上一次請求的數據,那麼如何將上一次請求和下一次請求的數據關聯起來呢?比如登錄官網後,再切換到其他頁面時候,那麼其他頁面是如何知道該用戶已經登陸了呢?所以這就可以使用到cookie中的值來判斷了。

cookie它是一個由瀏覽器和服務器共同協作實現的協議的。那麼cookie分爲如下幾步實現:

1.服務端向客戶端發送cookie

2.瀏覽器將cookie保存

3.之後每次請求都會將cookie發向服務端

2、Session

cookie操作很方便,但是使用cookie安全性不高,cookie中的所有數據在客戶端可以被修改,數據很容易僞造;所以一些重要的數據就不能放在cookie當中了,並且cookie還有一個缺點就是不能存放太多的數據,爲了解決這些問題,session就產生了,session中的數據保留在服務端的。

2.1 基於Cookie來實現用戶和數據的映射

把數據放到cookie中是不可取的,但是我們可以將口令放在放在cookie中的,比如cookie中常見的會放入一個sessionId,該sessionId會與服務端之間會產生映射關係,如果sessionId被篡改的話,那麼它就不會與服務器數據之間產生映射,因此安全性就更好,並且session的有效期一般比較短,一般都是設置是20分鐘,如果在20分鐘內客戶端與服務端沒有沒有發生交互,服務端就會將數據刪除。

session的原理是通過一個seesionId來進行的,sessionId是放在客戶端的cookie中,當請求到來的時候,服務端會檢查cookie中保存的sessionId是否有, 並且與服務端的session data映射起來,進行數據的保存和修改,也就是說當我們瀏覽一個網頁時候,服務端會隨機生成一個1024比特長的字符串,然後存在
cookie中的sessionid字段中,當我們下次訪問時,cookie會帶有sessionid這個字段。

通過Session保存登錄狀態

在Express這個框架中,默認不支持Session和Cookie,但是我們可以使用第三方中間件:express-session 來解決

安裝

npm install express-session --save

配置(一定要在路由掛載之前)

var session = require('express-session')
app.use(session({
    secret:'keyboard cat',//secret: 通過設置secret字符串,來計算hash值並放在cookie中,使產生的signedCookie防篡改。
    resave:false,
    saveUninitialized:true //無論你是否使用session,我都默認直接給請求的域分配一把鑰匙,false時,只有使用session的時候,纔會分配鑰匙
}))

使用:當把這個插件配置好之後,我們就可以通過req.session來訪問和設置Session成員了。

添加Session數據:req.session.foo = ‘bar’

訪問Session數據:req.session.foo

刪除Session數據的某個成員:delete req.session.foo

提示:默認Seesion數據是內存存儲的,服務器一旦重啓就會丟失。真正的生產環境中會把Session進行持久化存儲。

寫案例步驟

目錄結構

.
|—— app.js 項目的入口文件
|—— controller 
|—— models 存儲使用mongoose
|—— node_modules 第三方包
|—— package.json 包描述文件
|—— package-lock.json 第三方包鎖定版本(npm5 以後纔有)
|—— public 公共的靜態資源
|—— README.md 項目說明文檔
|—— routers 如果業務比較多,代碼量大,最好把路由按照業務的分類存儲到routers
|—— router.js 簡單一點把所有的路由都放到這個文件
|—— views 存儲視圖目錄

  • 創建目錄結構
  • 整合靜態頁 - 模板頁
    • include
    • block
    • extend
  • 設計用戶登錄、退出、註冊的路由
  • 用戶註冊
    • 先處理好客戶端頁面的內容(表單控件的name、手機表單數據、發起請求)
    • 服務端
      • 獲取客戶端表單請求數據
      • 操作數據庫
      • 如果有錯誤,發送500告訴客戶端服務器錯了
      • 其它的根據業務發送不同的響應數據
  • 用戶登錄
  • 用戶退出

Nodejs原生中間件的概念

在Node中的原生代碼裏,我們必須自己一步一步的去解析請求的url來獲取響應的數據和響應數據格式(如:解析表單中get請求、解析表單 post 請求體、解析 Cookie、處理 Session、使用模板引擎),如果自己一步一步的寫,過於麻煩,於是就有很多大神寫了很多的第三方包來供我們使用,如下原生代碼的中間件概念。

以下文件均在同一個文件夾下

先創建middlewares文件夾,後在該文件夾下,創建文件cookie.js、post-body.js、query.js、session.js四個文件,文件都是向外暴露一個函數,這裏只寫query.js文件的內容。

module.exports = function (req) {
  req.query = {}
}

Express中間件

中間件:處理請求的,本質上就是個函數

在 Express 中,對中間件有幾種分類,如下:

  • 不關心請求路徑和請求方法的中間件,也就是說任何請求都會進入這個中間件

    • app.use(function (req, res, next) {
      	console.log('1')
      	next()
      })
      
      
  • 以 /xxx 開頭的路徑中間件

    • app.use('/a', function (req, res, next) {
         console.log('a')
         next()
      })
      
      
  • 嚴格匹配請求方法和請求路徑的中間件

    • app.get('/abc', function (req, res, next) {
        console.log('abc')
        next()
      })
      
      

中間件本身是一個方法,該方法接收三個參數: Request 請求對象、Response 響應對象、next 下一個中間件

當請求進,會從第一個中間件開始匹配

​ 如果匹配,則進來

​ 如果請求進入中間件之後,沒有調用 next 則代碼會停在當前中間件

​ 如果調用了 next 則繼續向後找到第一個匹配的中間件

​ 如果不匹配,則繼續判斷匹配下一個中間件

注意:當一個請求進入一箇中間件之後,如果不調用 next 則會停留在當前中間件,所以 next 是一個方法,用來調用下一個中間件的,調用 next 方法也是要匹配的(不是調用緊挨着的那個)

如果沒有能匹配的中間件,則 Express 會默認輸出:Cannot GET 路徑

案例(很重要):

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

app.get('/abc', function (req, res, next) {
   req.foo = 'bar'
   req.body = {}
   next() //調用下一次符合匹配的中間件
})
 app.get('/abc', function (req, res, next) {
   console.log(req.foo) //可以在這裏使用上一個中間裏添加的變量
   console.log(req.body)
})

app.get('/', function (req, res, next) {
  fs.readFile('.d/sa./d.sa/.dsa', function (err, data) {
    if (err) {
      // return res.status(500).send('Server Error')
      // 當調用 next 的時候,如果傳遞了參數,則直接往後找到帶有 四個參數的應用程序級別中間件
      // 當發生錯誤的時候,我們可以調用 next 傳遞錯誤對象
      // 然後就會被全局錯誤處理中間件匹配到並處理之
      next(err)
    }
  })
})

app.get('/', function (req, res, next) {
  console.log('/ 2')
})



app.get('/a', function (req, res, next) {
  fs.readFile('./abc', function (err, data) {
    if (err) {
      // return res.status(500).send('Server Error') 
      next(err)
    }
  })
})

app.use(function (req, res, next) {
  res.send('404')
})

// 配置錯誤處理中間件
app.use(function (err, req, res, next) {
  res.status(500).send(err.message)
})

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

注意這裏的一句話:當調用 next 的時候,如果傳遞了參數,則直接往後找到帶有 四個參數的應用程序級別中間件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章