有了 [Node.js] 07 - Html and Http 做爲基礎,再繼續下面的內容。javascript
順便再介紹一個:adminlithtml
Ref: REST API 最佳入門指南java
Ref: RPC vs RESTful【思考】node
Ref: WEB開發中,使用JSON-RPC好,仍是RESTful API好?【思考】android
REST是Representational State Transfer的縮寫,描述建立HTTP API的標準方法。web
他發現這四種經常使用的行爲(查看(view),建立(create),編輯(edit)和刪除(delete))均可以直接映射到HTTP 中已實現的GET, POST, PUT 和 DELETE方法。數據庫
HTTP 中的8中不一樣的方法:GET,POST,PUT,DELETE, OPTIONS, HEAD, TRACE,CONNECTexpress
大多數狀況下,當你在使用你的瀏覽器的點點看看的時候,其實只用到HTTP的GET方法。GET方法是在你向因特網請求資源的時候纔會用到的。json
當你提交一個表單時,你就會常常用到POST方法來回傳數據到網站上。api
至於其餘的幾種方法,某些瀏覽器可能根本就沒有去徹底實現它們。可是,若是是供咱們使用的話,就沒什麼問題。
問題是:咱們有不少要選擇去幫助描述這四大行爲的HTTP方法,咱們將會用到那些已經知道如何去使用這些不一樣的HTTP方法的客戶端類庫。
【感受也沒說什麼】
REST即表述性狀態傳遞(英文:Representational State Transfer,簡稱REST)是Roy Fielding博士在2000年他的博士論文中提出來的一種軟件架構風格。
表述性狀態轉移是一組架構約束條件和原則。知足這些約束條件和原則的應用程序或設計就是RESTful。【什麼該作,什麼不應作】
參見:RESTful 架構詳解
舉例說明:
須要注意的是,REST是設計風格而不是標準。REST一般基於使用HTTP,URI,和XML(標準通用標記語言下的一個子集)以及HTML(標準通用標記語言下的一個應用)這些現有的普遍流行的協議和標準。
(1) REST 一般使用 JSON 數據格式。
{
"user1" : {
"name" : "mahesh",
"password" : "password1",
"profession" : "teacher",
"id": 1
},
"user2" : {
"name" : "suresh",
"password" : "password2",
"profession" : "librarian",
"id": 2
},
"user3" : {
"name" : "ramesh",
"password" : "password3",
"profession" : "clerk",
"id": 3
}
}
(2) 基於以上數據,咱們建立如下 RESTful API:
回想:(查看(view),建立(create),編輯(edit)和刪除(delete))均可以直接映射到HTTP 中已實現的GET, POST, PUT 和 DELETE方法。
咱們要爲路由提供請求的 URL 和其餘須要的 GET 及 POST 參數,隨後路由須要根據這些數據來執行相應的代碼。所以,咱們須要查看 HTTP 請求,從中提取出請求的 URL 以及 GET/POST 參數。
咱們須要的全部數據都會包含在request 對象中,該對象做爲 onRequest() 回調函數的第一個參數傳遞。
解析這些數據: url 和 querystring 模塊。
var http = require("http");
var url = require("url"); // 解析協議的模塊
function start() {
// 定義了一個處理request的方法
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(pathname); // ----> 能夠作一個if...else來判斷,封裝在另外一個文件中
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
router.js 的文件
function route(pathname) {
console.log("About to route a request for " + pathname);
}
exports.route = route;
From: 輕鬆學 Node.js - 基礎篇 #14 web 服務器 part 6 重構路由代碼
這部分也算是以上的一個總結,功能分離的一個小例子。
app.js - 使用的地方
var server = require('./server'); var router = require('./router'); var handler = require('./handler'); var handle = {}; handle["/"] = handler.home; handle['/home'] = handler.home; handle['/review'] = handler.review; handle['/api/v1/records'] = handler.api_records; server.startServer(router.route, handle); // 粘貼函數
server.js - 黏貼函數的實現
var http = require('http'); var fs = require('fs'); function startServer(route, handle) {
var onRequest = function(request, response) { console.log('Request received ' + request.url);
/* */ route(handle, request.url, response); // ---> 外殼要執行具體路由功能 } var server = http.createServer(onRequest); server.listen(3000, '127.0.0.1'); console.log('Server started on localhost port 3000'); } module.exports.startServer = startServer;
router.js - 路由執行函數
var fs = require('fs'); function route(handle, pathname, response) { console.log('Routing a request for ' + pathname);
if (typeof handle[pathname] === 'function') { handle[pathname](response); } else { response.writeHead(200, { 'Content-Type': 'text/html' }); fs.createReadStream(__dirname + '/404.html', 'utf8').pipe(response); } } module.exports.route = route;
handler.js - 路由時的Index
var fs = require('fs'); function home(response) { response.writeHead(200, { 'Content-Type': 'text/html' }); fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(response); } function review(response) { response.writeHead(200, { 'Content-Type': 'text/html' }); fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(response); } function api_records(response) { response.writeHead(200, { 'Content-Type': 'application/json' }); var jsonObj = { name: "hfpp2012" }; response.end(JSON.stringify(jsonObj)); } module.exports = { home: home, review: review, api_records: api_records }
服務器都須要跟用戶的瀏覽器打交道,如表單提交。
var http = require('http'); var url = require('url' ); var util = require('util'); http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'}); res.end(util.inspect( url.parse(req.url, true) )); // util.inspect: 將任意對象轉換 爲字符串的方法
}).listen(3000);
var http = require('http');
var url = require('url ');
var util = require('util');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
// 解析 url 參數
var params = url.parse(req.url, true).query;
// 寫返回結果,以後傳給瀏覽器
res.write("網站名:" + params.name);
res.write("\n");
res.write("網站 URL:" + params.url);
res.end();
}).listen(3000);
POST 請求的內容所有的都在請求體中,http.ServerRequest 並無一個屬性內容爲請求體,緣由是等待請求體傳輸多是一件耗時的工做。
好比上傳文件,而不少時候咱們可能並不須要理會請求體的內容,惡意的POST請求會大大消耗服務器的資源,因此 node.js 默認是不會解析請求體的,當你須要的時候,須要手動來作。
var http = require('http');
var querystring = require('querystring');
http.createServer( function(req, res) {
// 定義了一個post變量,用於暫存請求體的信息
var post = '';
// 經過req的data事件監聽函數,每當接受到請求體的數據,就累加到post變量中
req.on('data', function(chunk){
post += chunk;
});
// 在end事件觸發後,經過querystring.parse將post解析爲真正的POST請求格式,而後向客戶端返回。
req.on('end', function(){ // --> 接收包的最後部分 接收完畢
post = querystring.parse(post);
res.end(util.inspect(post)); // --> 接收完畢,那就稍加處理下這裏是即刻迴應客戶端
});
}).listen(3000);
var http = require('http'); var querystring = require('querystring');
---------------------------------------------------------------------------------- var postHTML = '<html><head><meta charset="utf-8"><title>菜鳥教程 Node.js 實例</title></head>' + '<body>' + '<form method="post">' + '網站名: <input name="name"><br>' + '網站 URL: <input name="url"><br>' + '<input type="submit">' + '</form>' + '</body></html>'; ----------------------------------------------------------------------------------
http.createServer(function (req, res) {
var body = "";
req.on('data', function (chunk) { body += chunk; });
req.on('end', function () { // 解析參數 body = querystring.parse(body);
// 設置響應頭部信息及編碼 res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'}); if(body.name && body.url) { // 輸出提交的數據 res.write("網站名:" + body.name); res.write("<br>"); res.write("網站 URL:" + body.url); } else { // 輸出表單 res.write(postHTML); } res.end(); });
}).listen(3000);
server.js
var http = require('http'); var fs = require('fs'); var url = require('url'); var querystring = require('querystring'); function startServer(route, handle) {
var onRequest = function(request, response) {
var pathname = url.parse(request.url).pathname; console.log('Request received ' + pathname); var data = []; request.on("error", function(err) { console.error(err); }).on("data", function(chunk) { data.push(chunk); }).on('end', function() {
----------------------------------------------------------------- if (request.method === "POST") { // ----> if (data.length > 1e6) { request.connection.destroy(); } data = Buffer.concat(data).toString(); route(handle, pathname, response, querystring.parse(data)); } else { var params = url.parse(request.url, true).query; route(handle, pathname, response, params); }
----------------------------------------------------------------- }); } var server = http.createServer(onRequest); server.listen(3000, '127.0.0.1'); console.log('Server started on localhost port 3000'); } module.exports.startServer = startServer;
handler.js
var fs = require('fs'); function home(response) { response.writeHead(200, { 'Content-Type': 'text/html' }); fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(response); } function review(response) { response.writeHead(200, { 'Content-Type': 'text/html' }); fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(response); } function api_records(response, params) { response.writeHead(200, { 'Content-Type': 'application/json' }); response.end(JSON.stringify(params)); }
function api_records(response) {/**
response.writeHead(200, { 'Content-Type': 'application/json' });
var jsonObj = { name: "hfpp2012" }; response.end(JSON.stringify(jsonObj)); }
**/
module.exports = { home: home, review: review, api_records: api_records }
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hfpp2012</title>
</head>
<body>
<form action="/api/v1/records" method="post">
name: <input type="text" name="name" /> age: <input type="text" name="age" />
<input type="submit" value="Submit">
</form>
</body>
</html>
目前最主流的三個Web服務器是Apache、Nginx、IIS。
* Client - 客戶端,通常指瀏覽器,瀏覽器能夠經過 HTTP 協議向服務器請求數據。
* Server - 服務端,通常指 Web 服務器,能夠接收客戶端請求,並向客戶端發送響應數據。
* Business - 業務層, 經過 Web 服務器處理應用程序,如與數據庫交互,邏輯運算,調用外部程序等。
* Data - 數據層,通常由數據庫組成。
http 模塊主要用於搭建 HTTP 服務端和客戶端。
服務器:返回一個簡單的頁面 index.html 文件
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>菜鳥教程(runoob.com)</title> </head> <body> <h1>個人第一個標題</h1> <p>個人第一個段落。</p> </body> </html>
Server code:
var http = require('http');
var fs = require('fs');
var url = require('url');
// 建立服務器
http.createServer( function (request, response) {
// 解析請求,包括文件名
var pathname = url.parse(request.url).pathname;
// 輸出請求的文件名
console.log("Request for " + pathname + " received.");
// 從文件系統中讀取請求的文件內容,客戶端給出請求的文件index.html路徑
fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
console.log(err);
// HTTP 狀態碼: 404 : NOT FOUND // Content Type: text/plain
response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 狀態碼: 200 : OK // Content Type: text/plain
response.writeHead(200, {'Content-Type': 'text/html'});
// 響應文件內容
response.write(data.toString());
}
// 發送響應數據
response.end();
});
}).listen(8080);
// 控制檯會輸出如下信息
console.log('Server running at http://127.0.0.1:8080/');
瀏覽器:請求 index.html 文件
var http = require('http');
// 用於請求的選項
var options = {
host: 'localhost',
port: '8080',
path: '/index.html'
};
// 處理響應的回調函數
var callback = function(response) {
// 不斷更新數據
var body = '';
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
// 數據接收完成
console.log(body);
});
}
// 向服務端發送請求
var req = http.request(options, callback);
req.end();
運行代碼
$ node server.js Server running at http://127.0.0.1:8080/ $ node client.js <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>菜鳥教程(runoob.com)</title> </head> <body> <h1>個人第一個標題</h1> <p>個人第一個段落。</p> </body> </html>
對比可見,改變了response.end參數的內容,便是支持了JSON。
其實仍是nodejs提供的本來的request和response對象:
req.app:當callback爲外部文件時,用req.app訪問express的實例
req.baseUrl:獲取路由當前安裝的URL路徑
req.body / req.cookies:得到「請求主體」/ Cookies
req.fresh / req.stale:判斷請求是否還「新鮮」
req.hostname / req.ip:獲取主機名和IP地址
req.originalUrl:獲取原始請求URL
req.params:獲取路由的parameters
req.path:獲取請求路徑
req.protocol:獲取協議類型
req.query:獲取URL的查詢參數串
req.route:獲取當前匹配的路由
req.subdomains:獲取子域名
req.accepts():檢查可接受的請求的文檔類型
req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一個可接受字符編碼
req.get():獲取指定的HTTP請求頭
req.is():判斷請求頭Content-Type的MIME類型
res.app:同req.app同樣
res.append():追加指定HTTP頭
res.set()在res.append()後將重置以前設置的頭
res.cookie(name,value [,option]):設置Cookie
opition: domain / expires / httpOnly / maxAge / path / secure / signed
res.clearCookie():清除Cookie
res.download():傳送指定路徑的文件
res.get():返回指定的HTTP頭
res.json():傳送JSON響應
res.jsonp():傳送JSONP響應
res.location():只設置響應的Location HTTP頭,不設置狀態碼或者close response
res.redirect():設置響應的Location HTTP頭,而且設置狀態碼302
res.render(view,[locals],callback):渲染一個view,同時向callback傳遞渲染後的字符串,若是在渲染過程當中有錯誤發生next(err)將會被自動調用。callback將會被傳入一個可能發生的錯誤以及渲染後的頁面,這樣就不會自動輸出了。
res.send():傳送HTTP響應
res.sendFile(path [,options] [,fn]):傳送指定路徑的文件 -會自動根據文件extension設定Content-Type
res.set():設置HTTP頭,傳入object能夠一次設置多個頭
res.status():設置HTTP狀態碼
res.type():設置Content-Type的MIME類型
路由決定了由哪一個指定腳本去響應客戶端請求。
超級簡單的hello world,擴展有點小功能(switch)的服務器。
例如:在瀏覽器中訪問 http://127.0.0.1:8081/list_user。
var express = require('express');
var app = express();
// 主頁輸出 "Hello World"
app.get('/', function (req, res) {
console.log("主頁 GET 請求");
res.send('Hello GET');
})
// POST 請求
app.post('/', function (req, res) {
console.log("主頁 POST 請求");
res.send('Hello POST');
})
// /del_user 頁面響應
app.get('/del_user', function (req, res) {
console.log("/del_user 響應 DELETE 請求");
res.send('刪除頁面');
})
// /list_user 頁面 GET 請求
app.get('/list_user', function (req, res) {
console.log("/list_user GET 請求");
res.send('用戶列表頁面');
})
// 對頁面 abcd, abxcd, ab123cd, 等響應 GET 請求
app.get('/ab*cd', function(req, res) {
console.log("/ab*cd GET 請求");
res.send('正則匹配');
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("應用實例,訪問地址爲 http://%s:%s", host, port)
})
諸如:圖片, CSS,JavaScript 等。
咱們能夠到 public/images 目錄下放些圖片,以下所示:
node_modules
server.js
public/
public/images
public/images/logo.png
Server coding:
var express = require('express');
var app = express();
app.use( express.static('public') );
app.get('/', function (req, res) {
res.send('Hello World');
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("應用實例,訪問地址爲 http://%s:%s", host, port)
})
在瀏覽器中訪問
http://127.0.0.1:8081/images/logo.png
process_get 路由器來處理輸入
(1) 瀏覽器訪問 http://127.0.0.1:8081/index.htm
(2) 找到index.htm文件
app.use(express.static('public'));
app.get('/index.htm', function (req, res) {
res.sendFile( __dirname + "/" + "index.htm" );
})
(3) 用戶得到index.htm 文件代碼,而後點擊按鈕,觸發action。
<html> <body> <form action="http://127.0.0.1:8081/process_get" method="GET"> First Name: <input type="text" name="first_name"> <br> Last Name: <input type="text" name="last_name"> <input type="submit" value="Submit"> </form> </body> </html>
(4) 變量賦值後返回給client新的頁面。
app.get('/process_get', function (req, res) {
// 輸出 JSON 格式
var response = {
"first_name":req.query.first_name,
"last_name" :req.query.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})
(3) 不一樣的路由函數結果。
<html> <body> <form action="http://127.0.0.1:8081/process_post" method="POST"> First Name: <input type="text" name="first_name"> <br> Last Name: <input type="text" name="last_name"> <input type="submit" value="Submit"> </form> </body> </html>
(4) 執行成爲了app.post(...)
app.post('/process_post', urlencodedParser, function (req, res) {
// 輸出 JSON 格式
var response = {
"first_name":req.body.first_name,
"last_name":req.body.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})
<html> <head> <title>文件上傳表單</title> </head> <body> <h3>文件上傳:</h3> 選擇一個文件上傳: <br /> <form action="/file_upload" method="post"enctype="multipart/form-data"> <input type="file" name="image" size="50" /> <br /> <input type="submit" value="上傳文件" /> </form> </body> </html>
服務器代碼:
var express = require('express');
var app = express();
var fs = require("fs");
var bodyParser = require('body-parser');
var multer = require('multer');
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ dest: '/tmp/'}).array('image'));
app.get('/index.htm', function (req, res) {
res.sendFile( __dirname + "/" + "index.htm" );
})
app.post('/file_upload', function (req, res) {
console.log(req.files[0]); // 上傳的文件信息
var des_file = __dirname + "/" + req.files[0].originalname;
fs.readFile( req.files[0].path, function (err, data) {
fs.writeFile(des_file, data, function (err) {
if( err ){
console.log( err );
}else{
response = {
message:'File uploaded successfully',
filename:req.files[0].originalname
};
}
console.log( response );
res.end( JSON.stringify( response ) );
});
});
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("應用實例,訪問地址爲 http://%s:%s", host, port)
})
使用中間件向 Node.js 服務器發送 cookie 信息,如下代碼輸出了客戶端發送的 cookie 信息:
// express_cookie.js 文件
var express = require('express')
var cookieParser = require('cookie-parser')
var util = require('util');
var app = express()
app.use(cookieParser())
app.get('/', function(req, res) {
console.log("Cookies: " + util.inspect(req.cookies));
})
app.listen(8081)