使用noode.js建立一個服務器

1、簡單的靜態服務器

一、代碼解析

var http = require('http')
// http是nodejs裏面的一個模塊,這個對象可以提供實現底層的方法。咱們經過require去加載這個模塊

var server = http.createServer(function(req, res){
  // 函數內部建立一個服務器,建立好以後,經過瀏覽器訪問這個服務器的時候,會把請求封裝成一個對象
  // 這個對象就是這個回調函數的第一個參數req。用戶請求的信息都在這個對象內,能夠獲取用戶的信息,如ip,請求信息等。
  // 第二個參數res是服務器返回給用戶的信息
    console.log('jiengu')
    res.setHeader("Content-Type","text/html; charset=utf-8")
  //設置響應頭的content-type內容,text/html是把響應體當成html解析,
    res.write('<h1> 飢人谷</h1>')
  //在res寫入服務器返回給瀏覽器的內容
    res.end()
})
server.listen(9000)
// 經過listen方法來啓動他,服務器監聽9000端口

二、執行步驟

打開gitbash,切換到js文件當前的文件夾,而後輸入node index.js(index.js是個人js文件名,反正大家取什麼名就輸入啥名)css

clipboard.png
打開瀏覽器,輸入http://127.0.0.1:9000/,或者http://localhost:9000/
注意哈9000是代碼裏面寫的9000端口,若是下次改爲了8080等其餘的端口,那就改爲對應的端口就好
clipboard.pnghtml

三、響應頭和響應體

響應頭查看路徑:network-name-headers
clipboard.png前端

響應體:
響應體是response的數據,有點相似於打開網頁的查看源代碼
clipboard.pngnode

每次修改了js文件的內容以後,要斷掉git的服務器,從新鏈接。否則即便刷新網頁沒有辦法顯示修改的內容git

四、設置響應頭

4.1response.setHeader
格式:response.setHeader(name, value)
爲一個隱式的響應頭設置值。 若是該響應頭已存在,則值會被覆蓋。 若是要發送多個名稱相同的響應頭,則使用字符串數組。 非字符串的值會保留原樣,因此 response.getHeader() 會返回非字符串的值。 非字符串的值在網絡傳輸時會轉換爲字符串。
舉例:github

response.setHeader('Content-Type', 'text/plain'); //當成字符串解析
response.setHeader('Content-Type','text/html; charset=utf-8')//當成html解析,若是是css就設置爲text/css

執行結果
clipboard.pngajax

setHeader引伸的連接,是nodejs中文網的規範json

4.2 response.writeHead()
writeHead文檔規範
格式:response.writeHead(statusCode, statusMessage)
參數1 statusCode(狀態碼)是一個三位數的 HTTP 狀態碼,如 404。
參數2是 statusMessage 是可選的狀態描述,是一個string。
參數3 headers 是響應頭,是個對象。其實咱們能夠理解爲這個對象放的是response headers所有內容。咱們設置的writehead的內容處理status碼是放在general,其餘的內容都是封裝成一個對象放在響應頭內容response headers。api

clipboard.png

response.writeHead(404, 'Not Found')
res.writeHead(200,'hhh', { 'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});

4.3二者的不一樣數組

  • response.writeHead() 在消息中只能被調用一次,且必須在 response.end() 被調用以前調用。調用兩次就會報錯。 setheader能夠屢次調用
  • headers.setheader()只容許您設置單一標題。
    writehead()容許您設置關於響應頭的幾乎全部內容,包括狀態代碼、內容和多個標題。

4.4遇到的坑
坑1:res.setHeader("Content-Type","text/html; charset=gbk")纔是對的,charset=gbk必須放在Content-Type內部,展現的時候也是在一塊兒。(我猜測charset應該是Content-Type的一部分)

clipboard.png

若是分開寫成下面的格式,不會報錯,但charset就變成了響應頭的單獨子項展現,並且charset=utf-8不會生效(下圖utf-8沒有生效就按照gbk去解碼,就出現了亂碼)。

res.setHeader('Content-Type', 'text/html');
res.setHeader("charset","utf-8")

clipboard.png
因此必定注意寫法

坑2:writeHead只能寫一次,全部響應頭要設置的內容都要按照對象的格式,放在參數三headers裏面。如下縮寫是正確的,要記住啊

res.writeHead(200,'hhh', { 'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});

坑3:response.setHeader() 設置的響應頭會與 response.writeHead() 設置的響應頭合併,可是若是設置的內容重複,以response.writeHead() 的優先爲準。

var server = http.createServer(function(req, res){
    res.setHeader("Content-Type","text/html; charset=utf-8")
    res.setHeader('X-Foo', 'bar');
    res.writeHead(200,'hhh', { 'Content-Type':'text/plain;charset=utf-8','X-Foo':'bar2222'});

    res.write('<h1> 飢人谷2</h1>')
    res.end()
})
server.listen(9000)

執行結果是:很明顯的看到setHeader和writeHead重複設置的內容,都是以writeHead爲準的

clipboard.png

4.5設置status的異常

res.writeHead(404,'hhh');

當我設置status爲404,發現即便是請求成功回送以後,也會出現紅色。這是由於你們約定404就是一個錯誤的狀態,因此status的值要按照約定來設置
clipboard.png

2、一個可用的靜態服務器

搭建一個有圖片,css,js的資源的服務器,github代碼連接

一、步驟

  1. 我在step1文件夾下放置了server.js文件,static文件夾。static文件夾對應放了css,png,js,html等文檔,並在html文檔內引用了圖片,css,js資源。
  2. 打開gitbash,切換step1文件夾,執行node server.jsclipboard.png
  3. 打開瀏覽器輸入localhost:8080index.html,查看結果

clipboard.png

輸出內容
clipboard.png

clipboard.png

二、js代碼解析

var http = require('http')
var path = require('path')
// path模塊處理url,不一樣系統(mac/lincx/window)下對url的寫法可能不一致的。(一個寫成c:/project/code/a.png
// 另一個可能寫成/user/local/project/a.png)。path模塊會對這種狀況自動處理url類型
var fs = require('fs')
// fs模塊用來讀取文件數據,也能夠往文件裏面寫數據。
var url = require('url')
// url模塊能夠自動解析url,獲得一個對象,能夠得到對應的信息。




function staticRoot(staticPath, req, res){
  console.log(staticPath) 
  //輸出static文件的絕對路徑,/user/documents/code/node-server/step1/static
  console.log(req.url) 
  //請求的url地址,第一次調用html時,爲/index.html,第二次調用css時,就是css/a.css
  var pathObj = url.parse(req.url, true)
  // 解析url,獲得url對象(包含protocal/hostname/port/pathname/query等等),即pathobj對象就是url的對象。本次要用的是pathname
  console.log(pathObj)
  
  
  if(pathObj.pathname === '/'){
    pathObj.pathname += 'index.html'
  }
  //若是pathname沒有輸入(瀏覽器輸入的值只是localhost:8080,沒有後綴的話),服務器默認選擇去讀取和發送index.html文件

  

  var filePath = path.join(staticPath, pathObj.pathname)
  // staticPath=static文件夾的絕對路徑, pathObj.pathname=調用文件的後綴地址。
  // 兩個加起來獲得filePath(用戶輸入的url想要訪問文件的絕對路徑),舉例本文是/user/documents/code/node-server/step1/static/index.html

  // var fileContent = fs.readFileSync(filePath,'binary')
  // res.write(fileContent, 'binary')
  // // 採用同步的方式讀取filePath的文檔,把讀取的數據寫入res對象內
  // res.end()
  
  
  fs.readFile(filePath, 'binary', function(err, fileContent){
  // 異步的方式來讀取filePath的文檔。binary指以二進制的方式來讀取數據,由於服務器不只僅要讀取普通的數據,須要兼容圖片和文件等數據。
    if(err){
      console.log('404')
      res.writeHead(404, 'not found')
      res.end('<h1>404 Not Found</h1>')
  // 在頁面展現404 Not Found。在res.end('數據')等於執行res.write('數據')加上res.end()
    }else{
      console.log('ok')
      res.writeHead(200, 'OK')
      res.write(fileContent, 'binary')
      // 經過二進制的方式發送數據
      res.end()      
    }
  })
  

}

console.log(path.join(__dirname, 'static'))

// 在瀏覽器輸入localhost:8080/index.html地址,瀏覽器向服務器發起請求。
// 服務器收到請求後,執行相關函數,解析req對象信息,獲得了index.html的地址。
// 服務器根據解析的地址在本地static文件夾下找到對應的index.html文件,讀取html裏面數據,並把數據放在res內,當成字符串發給服務器。

var server = http.createServer(function(req, res){
  staticRoot(path.join(__dirname, 'static'), req, res)  //寫一個staticRoot函數,來處理請求。
 /* 參數1:把哪一個路徑當成靜態文件路徑,傳遞路徑名。__dirname是nodejs裏面的一個變量,表明當前的server.js執行的這個文件。
  path.join(__dirname, 'static')可使用一個或多個字符串值參數,該參數返回將這些字符串值參數結合而成的路徑。
var joinPath = path.join(__dirname, 'a', 'b', 'c');
console.log(joinPath);      //   D:\nodePro\fileTest\a\b\c,
__dirname對應的step1文件夾的路徑,加上static文件夾得路徑,就等於static的絕對路徑。、
  這樣的好處是每次絕對路徑發生變化的時候,不用從新去修改絕對路徑。*/
})

server.listen(8080)  //建立一個服務器,監聽8080端口
console.log('visit http://localhost:8080' )

三、代碼難點解析

3.1 path node.js文檔中的標準解釋
path 模塊用於處理文件與目錄的路徑。不一樣系統(mac/lincx/window)下對url的寫法可能不一致的。(一個寫成c:/project/code/a.png
// 另一個可能寫成/user/local/project/a.png)。path模塊會對這種狀況自動處理url類型

3.2 path.join([...paths])
參數...paths <string> :路徑片斷的序列,返回: <string>
使用平臺特定的分隔符把全部 path 片斷鏈接到一塊兒,並規範化生成的路徑

path.join('C:\Users\jz\documents\code\node-server\step1'
, 'static')
//C:\Users\jz\documents\code\node-server\step1\static

3.3 fs 文件系統node.js文檔中的標準解釋
fs 模塊用於以一種相似標準 POSIX 函數的方式與文件系統進行交互。
全部的文件系統操做都有同步和異步兩種形式。
異步形式的最後一個參數是完成時的回調函數。 傳給回調函數的參數取決於具體方法,但第一個參數會保留給異常。 若是操做成功完成,則第一個參數會是 null 或 undefined。

3.4 fs.readFile(path[, options], callback)異步地讀取文件的內容
path 文件名或文件路徑
options 若是 options 是一個字符串,則指定字符編碼,默認爲 null
callback 是一個回調函數,有兩個參數 (err, data),其中 data 是要讀取文件的內容

fs.readFile(filePath, 'binary', function(err, fileContent){
  // 異步的方式來讀取filePath的文檔。binary指以二進制的方式來讀取數據,由於服務器不只僅要讀取普通的數據,須要兼容圖片和文件等數據。
    if(err){
      console.log('404')
      res.writeHead(404, 'not found')
      res.end('<h1>404 Not Found</h1>')
  // 在頁面展現404 Not Found。在res.end('數據')等於執行res.write('數據')加上res.end()
    }else{
      console.log('ok')
      res.writeHead(200, 'OK')
      res.write(fileContent, 'binary')
      // 經過二進制的方式發送數據
      res.end()      
    }
  })

3.5 fs.readFileSync(path[, options])
同步的讀取文件內容,兩個參數和異步的同樣的用法

// var fileContent = fs.readFileSync(filePath,'binary')
  // res.write(fileContent, 'binary')
  // // 採用同步的方式讀取filePath的文檔,把讀取的數據寫入res對象內
  // res.end()

3.6 url模塊node.js文檔中的標準解釋
url 模塊提供了一些實用函數,用於 URL 處理與解析。 URL 字符串能夠被解析爲一個 URL 對象,其屬性對應於字符串的各組成部分。

clipboard.png

3.7url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
url.parse() 方法會解析一個 URL 字符串並返回一個 URL 對象。
urlString <string>
要解析的 URL 字符串。
parseQueryString <boolean>
若是爲 true,則 query 屬性總會經過 querystring 模塊的 parse() 方法生成一個對象。 若是爲 false,則返回的 URL 對象上的 query 屬性會是一個未解析、未解碼的字符串。 默認爲 false。
slashesDenoteHost <boolean>
若是爲 true,則 // 以後至下一個 / 以前的字符串會被解析做爲 host。 例如,//foo/bar 會被解析爲 {host: 'foo', pathname: '/bar'} 而不是 {pathname: '//foo/bar'}。 默認爲 false。
舉個例子

var pathObj = url.parse(req.url, true)// 解析req.url,獲得url對象pathobj

clipboard.png
3.8__dirname
當前模塊的文件夾名稱。等同於 __filename 的 path.dirname() 的值
__filename 當前模塊的文件名稱---解析後的絕對路徑
例如:
在 /Users/mjr 目錄下執行 node example.js

console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr

四、坑

有一個問題,爲何咱們要用req.url解析成url對象pathobj,再經過staticPath文件地址和pathobj.pastname結合成filepath,爲啥咱們不直接把req.url和staticPath結合在一塊兒生成filepath呢?這樣還少了一步呢

答案:若是requrl是常規的index.html或者css.css這種,兩種方式都不會報錯。可是若是url比較複雜,像是index.html?query=111#111這種,直接把req.url和staticPath結合在一塊兒是會報錯的,因此須要轉成url對象再把pashname挑出來。

3、實現一個簡單的node.js服務器路由

實現更復雜的服務器,url不只僅是定位一個靜態文件,能夠mock任何數據和前端交互。

一、核心原理:

根據瀏覽器請求的不一樣路由,致使服務器執行不一樣的操做。

二、文檔結構:

clipboard.png

三、服務器實現3條路由:

  • /getWeather,結合b.js文件實現一個ajax來mock天氣數據
  • /user/123 ,結合user.tpl文件實現用戶頁面
  • /index.html,結合index.html實現index.html的頁面。在html引用css文件,b.js,和圖片

四、對應的文件內容

能夠查看GitHub上面的代碼,我這裏截圖說明

html
clipboard.png
css

clipboard.png
js,實現ajax的代碼

clipboard.png

user.tpl
clipboard.png
最重要的server-simple.js服務器代碼
本次演示的url是localhost:8080/user/123,localhost:8080以後的內容是路由。全部請求到8080這個服務器內,根據不一樣的路由給瀏覽器發送不一樣的數據

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



http.createServer(function(req, res){

  var pathObj = url.parse(req.url, true)
  console.log(pathObj)

  switch (pathObj.pathname){
    case '/getWeather':    //根據req.url來執行不一樣的函數
      var ret
      if(pathObj.query.city == 'beijing'){
        ret = {
          city: 'beijing',
          weather: '晴天'
        }
      }else{
        ret = {
          city: pathObj.query.city,
          weather: '不知道'
        }
      }
      res.setHeader('content-Type','text/plain;charset=utf-8')
      res.end(JSON.stringify(ret)) //給瀏覽器輸入是一個json格式的對象,根據JSON.stringify轉換成字符串
      break;
    case '/user/123':

      res.end( fs.readFileSync(__dirname + '/static/user.tpl' ))
      //若是路由是/user/123,讀取user.tpl的內容,並返回給瀏覽器
      break;
    default:
      res.end( fs.readFileSync(__dirname + '/static' + pathObj.pathname) )
  }
}).listen(8080)

五、執行結果

index.html
clipboard.png

/getWeather
clipboard.png

/user/123
clipboard.png

相關文章
相關標籤/搜索