1. 程式人生 > >node.js之前後端互動小樣例

node.js之前後端互動小樣例

我們寫個表單,來看下基於node.js的前後端互動。

先說明要做的事情: 表單中輸入資料提交後傳給服務端,服務端收到HTTP請求後響應,傳送資料給客戶端,客戶端輸出顯示(這裡不寫模板渲染了)

首先我們寫個html檔案建立一個表單,程式如下:

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主頁</title>
</head>
<body>
    <form>
        <input id="inputName" value="" />
        <input id="login" value="登陸" type="button">
    </form>
</body>
</html>

頁面非常簡單,不過要小心,這裡的按鈕是用input 設定type='button'完成的。

直接使用button可能會出錯,單擊按鈕自動重新整理。

見討論:http://bbs.csdn.net/topics/390307663

之後我們寫服務端程式,在瀏覽器輸入url後返回這個頁面:

server.js

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

var server =  new http.Server()

function sendfile( res, abspath, data ) {
    res.writeHead(
        200,
        {'content-type': 'text/html'}
    )
    res.end( data )
}

function serverStatic(res,abspath) {
    fs.readFile( abspath, function (err,data){
        sendfile( res, abspath, data )
    })
}
server.on( 'request', function (req, res) {
  console.log('-----接收到請求-----')
  serverStatic(res,'./home.html')
})

server.listen(2500)

下面對上面的程式做出說明:
首先我們需要匯入http模組和fs模組。fs模組是用來提供檔案系統相關功能的。
先建立一個Http.Server類例項,用該例項進行服務端操作。看下官方文件
http://nodejs.cn/api/http.html#http_class_http_server
官網第一句:該類繼承自 net.Server,且具有以下額外的事件:
這裡我們用到的是request事件,對於request事件,提供一個監聽函式,這個監聽函式有兩個引數
request,response。不要把監聽函式裡的request物件和request事件混為一談,request事件裡的request是事件名稱


request事件的監聽函式中的request物件是http.IncomingMessage例項,http.IncomingMessage是個可讀流,資料從外部流到node,
你可以讀取裡面的資料,可讀流可以自動讀取(on或者pipe),也可以手動讀取(read)。
request事件的監聽函式中的response物件是http.ServerPonse例項,用官網裡的話說http.ServerPons這個類實現了(而不是繼承自)可寫流 介面。
總結一下:在node是服務端的時候,req是從客戶端發起的,是可讀流,res是從node響應的,是可寫流。
之後我們再看下文件對http.ServerResponse 類的描述
該物件在 HTTP 伺服器內部被建立。 它作為第二個引數被傳入 'request' 事件。
它作為第二個引數被傳入‘request’事件也就是說它被作為‘request’事件的監聽函式的第二個引數傳入。server.on( 'request', function (req, res) {} )程式碼中的res就是http.ServerResponse類例項。
http.ServerResponse類有個方法,end方法,如下:


下面我們看使用fs模組獲取檔案內容
我們上管網看下如何使用fs模組讀取硬碟檔案
http://nodejs.cn/api/fs.html#fs_fs_readfile_path_options_callback


fs.readFile( abspath [,options], callback )有三個引數,其中第二個引數options引數可選, 第三個引數callback是回撥函式
第一個引數是檔名或者檔案描述符,可以傳入string,url,buffer,interager型別的值給path,這裡我們選的是絕對路徑..
回撥函式有兩個引數,回撥有兩個引數 (err, data),其中 data 是檔案的內容。
這樣使用fs.readFile可以獲取檔案內容了。然後我們還要將檔案內容作為響應的資料響應。因此需要這句res.end( data ),data就是檔案內容。
總結一下程式碼思路,fs獲取home.html檔案內容data,之後響應以data返回.
至此輸入url獲取頁面已經成功了,之後要做的就是傳送POST請求,服務端獲取請求的資料
我們獲取點選按鈕獲取input的value的值將其傳送給服務端,發起請求直接使用xhr物件即可,詳細的見js高階教程

直接給出程式碼:

var button = document.getElementById("login")
            var xhr = new XMLHttpRequest()
            var handler = function(event) {//點選後獲取input的值併發送
                var value = document.getElementById("inputName").value
//                console.log(value)
                xhr.open('post','/',true)//啟動請求,url可以是相對於執行程式碼的當前頁面
                xhr.send( value )
            }

button.addEventListener('click', handler )
為了接收post請求,修改server.on(  'request',  callback ),程式碼如下
server.on( 'request', function (req, res) {
  console.log('-----接收到請求-----')
    //res.end('hello')
   serverStatic(res,'./home.html')
    if( req.method=='POST' ){
      //console.log(req)
       console.log(req.rawHeaders)
        var data = ""
        req.on("data",function(chunk){
            data += chunk
        })
        req.on("end",function(){
           // console.log(data)
            res.writeHead(
                200,{'Content-Type':'text/plain'}
            )
             res.end('響應資料: '+data)
        })
    }
})

對上面程式碼做些說明,官網有下面的話:

http.IncomingMessage 類

檢視英文版 / 參與翻譯

新增於: v0.1.17

它實現了 可讀流 介面,還有以下額外的事件、方法、以及屬性

注意最後一句: 它實現了 可讀流 介面,還有以下額外的事件、方法、以及屬性

我們看下可寫流:

http://nodejs.cn/api/stream.html#stream_event_data
可寫流有一個data事件
'data' 事件#

檢視英文版 / 參與翻譯

新增於: v0.9.4
  • chunk <Buffer> | <string> | <any> 資料片段。對於非物件模式的可讀流,這是一個字串或者 Buffer。 對於物件模式的可讀流,這可以是除 null 以外的任意型別 JavaScript 值。

'data' 事件會在流將資料傳遞給消費者時觸發。當流轉換到 flowing 模式時會觸發該事件。呼叫 readable.pipe(), readable.resume() 方法,或為 'data'事件添加回調可以將流轉換到 flowing 模式。 'data' 事件也會在呼叫 readable.read() 方法並有資料返回時觸發。

在沒有明確暫停的流上新增 'data' 事件監聽會將流轉換為 flowing 模式。 資料會在可用時儘快傳遞給下個流程。

如果呼叫 readable.setEncoding() 方法明確為流指定了預設編碼,回撥函式將接收到一個字串,否則接收到的資料將是一個 Buffer 例項。

const readable = getReadableStreamSomehow();
readable.on('data', (chunk) => {
  console.log(`Received ${chunk.length} bytes of data.`);
});

是的。我們就是利用可寫流data事件獲取post請求內容的。'data' 事件會在流將資料傳遞給消費者時觸發,對應的監聽函式中的chunk是資料片段,因為訊息是一段一段傳送的。訊息傳送完成後,流中無資料可供消費,觸發end事件
'end' 事件#

檢視英文版 / 參與翻譯

新增於: v0.9.4

'end' 事件將在流中再沒有資料可供消費時觸發。

觸發end事件後,將拼接好的資料作為響應的一部分發送。

最後一步,客戶端收到需要響應後輸出,在home.html中新增如下程式碼即可:

xhr.onreadystatechange = function () {
                if( xhr.readyState==4  ){//已經接收全部響應資料
                    if( (xhr.status>=200&&xhr.status<300) || xhr.status==304 ){
                        console.log( '-----請求並響應成功------' )
                        console.log(xhr.responseText)
                        console.log( '-----請求並響應成功------' )
                    }else{
                        console.log( "請求未成功: " + xhr.status )
                    }
                }
            }

            xhr.onreadystatechange()

最後給出完整的程式碼:

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主頁</title>
</head>
<body>
    <form>
        <input id="inputName" value="" />
        <input id="login" value="登陸" type="button">
    </form>
    <script>
        window.onload = function () {
            var button = document.getElementById("login")
            var xhr = new XMLHttpRequest()
            var handler = function(event) {//點選後獲取input的值併發送
                var value = document.getElementById("inputName").value
//                console.log(value)
                xhr.open('post','/',true)//啟動請求,url可以是相對於執行程式碼的當前頁面
                xhr.send( value )
            }

            button.addEventListener('click', handler )

            xhr.onreadystatechange = function () {
                if( xhr.readyState==4  ){//已經接收全部響應資料
                    if( (xhr.status>=200&&xhr.status<300) || xhr.status==304 ){
                        console.log( '-----請求並響應成功------' )
                        console.log(xhr.responseText)
                        console.log( '-----請求並響應成功------' )
                    }else{
                        console.log( "請求未成功: " + xhr.status )
                    }
                }
            }

            xhr.onreadystatechange()
        }
    </script>
</body>
</html>

server.js

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

var server =  new http.Server()

function sendfile( res, abspath, data ) {
    res.writeHead(
        200,
        {'content-type': 'text/html'}
    )
    res.end( data )
}

function serverStatic(res,abspath) {
    fs.readFile( abspath, function (err,data){
        sendfile( res, abspath, data )
    })
}

server.on( 'request', function (req, res) {
  console.log('-----接收到請求-----')
    //res.end('hello')
   serverStatic(res,'./home.html')
    if( req.method=='POST' ){
      //console.log(req)
       console.log(req.rawHeaders)
        var data = ""
        req.on("data",function(chunk){
            data += chunk
        })
        req.on("end",function(){
           // console.log(data)
            res.writeHead(
                200,{'Content-Type':'text/plain'}
            )
             res.end('響應資料: '+data)
        })
    }
})

server.listen(2500)



參考 js高階教程

        node.js官網

流:https://segmentfault.com/a/1190000000519006?share_user=1030000004603185