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.4chunk
<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'
事件將在流中再沒有資料可供消費時觸發。
最後一步,客戶端收到需要響應後輸出,在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