1.什么是HTTP模塊
我們知道傳統(tǒng)的HTPP服務(wù)器會(huì)由Aphche、Nginx、IIS之類的軟件來擔(dān)任,但是nodejs并不需要椭赋,nodejs提供了http模塊,自身就可以用來構(gòu)建服務(wù)器或杠,而且http模塊是由C++實(shí)現(xiàn)的哪怔,性能可靠。大部分的node使用者,都是用node來做Web API的认境,而HTTP模塊是提供Web API的基礎(chǔ)胚委。為了支持所有的HTTP應(yīng)用,node中的HTTTP模塊提供的API是偏向底層化的叉信。利用HTTP模塊亩冬,我們可以簡單快速搭建一個(gè)Web Server。
2.搭建web服務(wù)器
node提供了http這個(gè)核心模塊(不用安裝哦硼身,直接require就可以了)硅急,用于創(chuàng)建http server服務(wù),使用下面代碼,輕松在本機(jī)的3000端口創(chuàng)建一個(gè)http服務(wù)器
下面我們來搭建一個(gè)簡易的http服務(wù)器
let http = require("http");
http.createServer(function(req,res){
res.writeHead(200,{
"content-type":"text/plain"
});
res.write("NodeJS學(xué)習(xí)之旅");
res.end();
}).listen(3000);
打開瀏覽器佳遂,輸入localhost:3000我們就可以看到屏幕上的"NodeJS學(xué)習(xí)之旅"了营袜,這表明這個(gè)最簡單的nodejs服務(wù)器已經(jīng)搭建成功了。
而上面的createServer方法中的參數(shù)函數(shù)中的兩個(gè)參數(shù)req和res則是分別代表了請求對象和響應(yīng)對象丑罪。
writeHead
方法的第一個(gè)參數(shù)表示HTTP的響應(yīng)狀態(tài)(200)表示一切正常荚板;第二個(gè)參數(shù)是“Content-Type”,表示我響應(yīng)給客戶端的內(nèi)容類型吩屹。
然再后我們調(diào)用了write
方法跪另,寫入我們需要傳遞給客戶端的內(nèi)容。最后一步我們調(diào)用了end
方法祟峦,表示此次請求已處理完成, end方法中也可以返回?cái)?shù)據(jù)罚斗。
.listen(port)
此函數(shù)有兩個(gè)參數(shù)徙鱼,第一個(gè)參數(shù)表示我們需要監(jiān)聽的端口宅楞,第二個(gè)參數(shù)是回調(diào)函數(shù)(其實(shí)是listening事件),當(dāng)監(jiān)聽開啟后立刻觸發(fā)袱吆。
上面的實(shí)例代碼使用的createServer方法返回了一個(gè)http.Server對象厌衙,這其實(shí)是一個(gè)創(chuàng)建http服務(wù)的捷徑,如果我們用以下代碼來實(shí)現(xiàn)的話绞绒,也將一樣可行
let http = require("http");
// 1.創(chuàng)建一個(gè)服務(wù)器實(shí)例對象
let sever = new http.Server();
// 2.注冊請求監(jiān)聽
sever.on("request", function (req, res) {
// writeHead方法的作用: 告訴瀏覽器返回的數(shù)據(jù)是什么類型的, 返回的數(shù)據(jù)需要用什么字符集來解析
res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8"
});
// end方法的作用: 結(jié)束本次請求, 并且返回?cái)?shù)據(jù)
res.end("NodeJS學(xué)習(xí)之旅");
});
// 指定監(jiān)聽的端口
sever.listen(3000);
以上代碼是通過直接創(chuàng)建一個(gè)http.Server對象婶希,然后為其添加request事件監(jiān)聽,其實(shí)也就說createServer方法其實(shí)本質(zhì)上也是為http.Server對象添加了一個(gè)request事件監(jiān)聽蓬衡。
createServer方法中的參數(shù)函數(shù)中的兩個(gè)參數(shù)req和res則是分別代表了請求對象和響應(yīng)對象喻杈。其中req是http.IncomingMessage的實(shí)例,res是http.ServerResponse的實(shí)例狰晚。
http.IncomingMessage
http.IncomingMessage是HTTP請求的信息筒饰,是后端開發(fā)者最關(guān)注的內(nèi)容,一般由http.Server的request事件發(fā)送壁晒,并作為第一個(gè)參數(shù)傳遞瓷们,包含三個(gè)事件
data
:當(dāng)請求體數(shù)據(jù)到來時(shí),該事件被觸發(fā),該事件提供一個(gè)參數(shù)chunk谬晕,表示接受的數(shù)據(jù)碘裕,如果該事件沒有被監(jiān)聽,則請求體會(huì)被拋棄攒钳,該事件可能會(huì)被調(diào)用多次(這與nodejs是異步的有關(guān)系)
end
:當(dāng)請求體數(shù)據(jù)傳輸完畢時(shí)帮孔,該事件會(huì)被觸發(fā),此后不會(huì)再有數(shù)據(jù)
close
:用戶當(dāng)前請求結(jié)束時(shí)夕玩,該事件被觸發(fā)你弦,不同于end,如果用戶強(qiáng)制終止了傳輸燎孟,也是用close
http.ServerResponse
http.ServerResponse是返回給客戶端的信息禽作,決定了用戶最終看到的內(nèi)容,一般也由http.Server的request事件發(fā)送揩页,并作為第二個(gè)參數(shù)傳遞旷偿,它有三個(gè)重要的成員函數(shù),用于返回響應(yīng)頭爆侣、響應(yīng)內(nèi)容以及結(jié)束請求
res.writeHead(statusCode,[heasers])
:向請求的客戶端發(fā)送響應(yīng)頭萍程,該函數(shù)在一個(gè)請求中最多調(diào)用一次,如果不調(diào)用兔仰,則會(huì)自動(dòng)生成一個(gè)響應(yīng)頭
res.write(data,[encoding])
:想請求的客戶端發(fā)送相應(yīng)內(nèi)容茫负,data是一個(gè)buffer或者字符串,如果data是字符串乎赴,則需要制定編碼方式忍法,默認(rèn)為utf-8,在res.end調(diào)用之前可以多次調(diào)用
res.end([data],[encoding])
:結(jié)束響應(yīng)榕吼,告知客戶端所有發(fā)送已經(jīng)結(jié)束饿序,當(dāng)所有要返回的內(nèi)容發(fā)送完畢時(shí),該函數(shù)必需被調(diào)用一次羹蚣,兩個(gè)可選參數(shù)與res.write()相同原探。如果不調(diào)用這個(gè)函數(shù),客戶端將用于處于等待狀態(tài)顽素。
3.http路徑分發(fā)
路徑分發(fā)也稱之為路由, 就是根據(jù)不同的請求路徑返回不同的數(shù)據(jù)
如何根據(jù)不同的請求路徑返回不同的數(shù)據(jù)?
通過請求監(jiān)聽方法中的request
對象, 我們可以獲取到當(dāng)前請求的路徑
通過判斷請求路徑的地址就可以實(shí)現(xiàn)不同的請求路徑返回不同的數(shù)據(jù)
let http = require("http");
// 1.創(chuàng)建一個(gè)服務(wù)器實(shí)例對象
let sever = http.createServer();
// 2.注冊請求監(jiān)聽
sever.on("request", function (req, res) {
res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8"
});
// startsWith方法在這里的作用是判斷url是否是以/index開頭
if (req.url.startsWith("/index")){
res.end("首頁");
} else if (req.url.startsWith("/login")){
res.end("登錄");
} else {
res.end("沒有數(shù)據(jù)");
}
});
// 3.指定監(jiān)聽的端口
sever.listen(3000);
打開瀏覽器咽弦,輸入localhost:3000的后面加上"/index"或者"/login"我們就能獲取到不同的數(shù)據(jù)
上面的代碼中, 因?yàn)閞eq對象是http.IncomingMessage 類的實(shí)例, 所以它可以使用這個(gè)類中方法.上面代碼中url
方法就是這個(gè)類的方法, url方法的作用是可以獲取到用戶請求的路徑
res對象其實(shí)是http.ServerResponse類的實(shí)例, 上面的end
方法其實(shí)是這個(gè)類的方法, end方法的作用是結(jié)束本次請求, 并且返回?cái)?shù)據(jù)
end方法和write方法都可以返回?cái)?shù)據(jù), 那么二者有什么不同呢?
如果通過end方法來返回?cái)?shù)據(jù), 那么只會(huì)返回一次
如果通過end方法來返回?cái)?shù)據(jù), 那么可以返回多次, 但是write方法不具有結(jié)束本次請求的功能, 所以還需要手動(dòng)調(diào)用end方法來結(jié)束本次請求
// 這里只會(huì)返回"首頁1"
res.end("首頁1");
res.end("首頁2");
// 這里會(huì)返回"首頁1"和"首頁2", 但是瀏覽器會(huì)一直停留在請求數(shù)據(jù)的狀態(tài)
res.write("首頁1");
res.write("首頁2");
// 還需要通過end方法結(jié)束請求
res.end();
4.響應(yīng)完整頁面
如何通過地址欄的路徑改變響應(yīng)不同的頁面, 可以在拿到用戶請求的路徑后利用fs模塊將對應(yīng)的網(wǎng)頁返回
示例:
在這個(gè)代碼文件同級文件夾下的www文件夾下面有index.html和login.html兩個(gè)文件, 通過瀏覽器地址欄localhost:3000后面的路徑跳轉(zhuǎn)到對應(yīng)的頁面
const http = require("http");
const path = require("path");
const fs = require("fs");
let sever = http.createServer();
sever.on("request", function (req, res) {
let filePath = path.join(__dirname, "www", req.url);
fs.readFile(filePath, "utf8", function (err, data) {
if (err) res.end("Sever Error");
res.end(data);
});
});
sever.listen(3000);
5.響應(yīng)靜態(tài)資源
在給瀏覽器返回?cái)?shù)據(jù)的時(shí)候, 如果沒有指定響應(yīng)頭的信息, 如果沒有設(shè)置返回?cái)?shù)據(jù)的類型, 那么瀏覽器不一定能正確的解析, 所以無論返回什么類型的靜態(tài)資源都需要添加對應(yīng)的響應(yīng)頭信息, 需要使用 MIME 來確定類型。
什么是MIME
MIME 是一種多用途 Internet 郵件擴(kuò)展(MIME)類型是用一種標(biāo)準(zhǔn)化的方式來表示文檔的 "性質(zhì)" 和 "格式"胁出。 簡單說, 瀏覽器通過 MIME 類型來確定如何處理文檔. 因此在響應(yīng)對象的頭部設(shè)置正確 MIME 類型是非常重要的.如果配置不正確型型,瀏覽器可能會(huì)曲解文件內(nèi)容,網(wǎng)站將無法正常工作划鸽,并且下載的文件也會(huì)被錯(cuò)誤處理输莺。
MIME 的組成結(jié)構(gòu)非常簡單: 由類型與子類型兩個(gè)字符串中間用 /
分隔而組成, 其中沒有空格. MIME 類型對大小寫不敏感戚哎,但是傳統(tǒng)寫法都是小寫.
例如:
-
text/plain
: 是文本文件默認(rèn)值。意思是 未知的文本文件 嫂用,瀏覽器認(rèn)為是可以直接展示的. -
text/html
: 是所有的HTML內(nèi)容都應(yīng)該使用這種類型. -
image/png
: 是 PNG 格式圖片的 MIME 類型.
在服務(wù)器中, 我們通過設(shè)置 Content-Type 這個(gè)響應(yīng)頭部的值, 來指示響應(yīng)回去的資源的 MIME 類型. 在 Node.js 中, 可以很方便的用響應(yīng)對象的 writeHead 方法來設(shè)置響應(yīng)狀態(tài)碼和響應(yīng)頭部.
MIME 有兩種默認(rèn)類型:
-
text/plain
表示文本文件的默認(rèn)值型凳。一個(gè)文本文件應(yīng)當(dāng)是人類可讀的,并且不包含二進(jìn)制數(shù)據(jù)嘱函。 -
application/octet-stream
表示所有其他情況的默認(rèn)值甘畅。一種未知的文件類型應(yīng)當(dāng)使用此類型。
如何使用MIME
首先我們需要獲取到準(zhǔn)備響應(yīng)給客戶端的文件的 后綴名.
要做到這一步我們可以通過req.url
拿到用戶輸入的路徑, 然后通過路徑模塊的.exname
方法獲取后綴名
let filePath = path.join(__dirname, "www", req.url);
let extName = path.extname(filePath);
獲取了文件后綴之后, 我們需要查找其對應(yīng)的 MIME 類型了. 這一步可以很輕松的使用第三方模塊 MIME 來實(shí)現(xiàn). 你可以自行去 NPM 上去查閱它的使用文檔.
但是我這里直接有mime.json的文件也就沒有去下載模塊了
需要這個(gè)文件的朋友可以點(diǎn)擊下載, 鏈接: https://pan.baidu.com/s/17yDEy2pb_hrdXJZSWYCwfw 提取碼: fkyq
最重要的東西 MIME 類型我們得到后. 接下來只要在響應(yīng)對象的 writeHead 方法里設(shè)置好 Content-Type 就行了.
const http = require("http");
const path = require("path");
const fs = require("fs");
const mime = require("./mime");
let sever = http.createServer();
sever.on("request", function (req, res) {
readFile(req, res);
});
sever.listen(3000);
function readFile(req, res) {
let filePath = path.join(__dirname, "www", req.url);
let extName = path.extname(filePath);
let type = mime[extName];
if (type.startsWith("text")){
type += "; charset=utf-8";
}
res.writeHead(200, {
"Content-Type" : type
});
/*
注意:
1.加載文本外的資源, 不能寫"utf8"
2.如果服務(wù)器在響應(yīng)數(shù)據(jù)的時(shí)候沒有指定響應(yīng)頭, 那么在有的瀏覽器可能無法響應(yīng)
* */
// fs.readFile(filePath, " utf8", function (err, data) {
fs.readFile(filePath, function (err, data) {
if (err) res.end("Sever Error");
res.end(data);
});
}
重構(gòu)代碼
現(xiàn)在來看看這個(gè)代碼, 是不是開始感覺有點(diǎn)亂糟糟的. 可以發(fā)現(xiàn), 整個(gè)靜態(tài)文件服務(wù)器的代碼就是在做一件事: 響應(yīng)回客戶端想要的靜態(tài)文件. 這段代碼職責(zé)單一, 且復(fù)用頻率很高. 那么我們有理由將其封裝成一個(gè)模塊.
具體的過程我就不贅述了. 以下是我的模塊代碼:
const path = require("path");
const fs = require("fs");
const mime = require("./mime");
function readFile(req, res, rootPath) {
let filePath = path.join(rootPath, req.url);
let extName = path.extname(filePath);
let type = mime[extName];
if (type.startsWith("text")){
type += "; charset=utf-8";
}
res.writeHead(200, {
"Content-Type" : type
});
fs.readFile(filePath, function (err, data) {
if (err) res.end("Sever Error");
res.end(data);
});
}
exports.StaticSever = readFile;
封裝好了模塊之后, 我們就可以刪去服務(wù)器代碼里那段讀取文件的代碼了, 直接引用模塊就行了. 以下是我修改后的代碼:
const http = require("http");
const path = require("path");
let ss = require("./15-StaticSever");
let sever = http.createServer();
sever.on("request", function (req, res) {
let rootPath = path.join(__dirname, "www");
ss.StaticSever(req, res, rootPath);
});
sever.listen(3000);
6.Get參數(shù)處理
由于GET請求直接被嵌入在路徑中,URL完整的請求路徑往弓,包括了?后面的部分疏唾,因此你可以手動(dòng)解析后面的內(nèi)容作為GET的參數(shù),Nodejs的url模塊中的parse函數(shù)提供了這個(gè)功能函似。
url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
將一個(gè)URL字符串轉(zhuǎn)換成對象并返回零酪。
urlString
要解析的url地址
parseQueryString
解析出來的查詢字符串還是查詢對象甲葬,true是對象 false是字符串
例如:http://foo/bar?a=123, true的話 query: {a: '123'}, false的話 query: 'a=123' 默認(rèn)是false
slashesDenoteHost
是否要解析出來host
例如://foo/bar 會(huì)被解析為{host: 'foo', pathname: '/bar},否則{pathname: '//foo/bar'}.默認(rèn)是false
const http = require('http');
const url = require('url');
let str = "http://root:123456@www.baidu.com:80/index.html?name=abc&age=34#search";
let obj = url.parse(str, true);
http.createServer((req, res) => {
let obj = url.parse(req.url, true);
res.end(obj.query.name + "---" + obj.query.age);
}).listen(3000);
url模塊中還有一個(gè)format方法, 作用是將對象解析為url地址
url.format(urlObject)
url.format({
protocol: 'https',
hostname: 'example.com',
pathname: '/some/path',
query: {
page: 1,
format: 'json'
}
});
// => 'https://example.com/some/path?page=1&format=json'
7.POST參數(shù)處理
用POST方式提交的數(shù)據(jù)會(huì)附帶在請求正文里面侄泽,所以我們需要獲取到附帶在request正文里的信息
用form表單提交數(shù)據(jù)
<form action="http://127.0.0.1:3000/index.html" method="post">
<input type="text" name="userName">
<input type="text" name="password">
<input type="submit" value="提交">
</form>
如何拿到POST請求傳遞過來的參數(shù)--使用querystring模塊
querystring.parse(str[, sep[, eq[, options]]])
將參數(shù)轉(zhuǎn)換為對象
str
欲轉(zhuǎn)換的字符串
sep
設(shè)置分隔符眨八,默認(rèn)為 ‘&'
eq
設(shè)置賦值符,默認(rèn)為 ‘='
[options] maxKeys
可接受字符串的最大長度蔑担,默認(rèn)為1000
let http = require("http");
let queryString = require("querystring");
let sever = http.createServer();
sever.on("request", function (req, res) {
// 定義變量保存?zhèn)鬟f過來的參數(shù)
let params = "";
// 注意 在NodeJS中 ,POST請求的參數(shù)我們不能一次性拿到, 必須分批獲取
req.on("data", function (chunk) {
params += chunk;
});
req.on("end", function () {
let obj = queryString.parse(params);
res.end(obj.userName + "---" + obj.password);
});
});
sever.listen(3000);
與get請求不同的是牌废,服務(wù)端接收post請求參數(shù)不是一次就可以獲取的,通常需要多次
post請求參數(shù)不能使用url模塊解析啤握,因?yàn)樗皇且粋€(gè)url鸟缕,而是一個(gè)請求體對象
querystring模塊中還有一個(gè)stringify方法, 作用是將對象轉(zhuǎn)換為參數(shù)
querystring.stringify(obj[, sep[, eq[, options]]])
將對象轉(zhuǎn)換為參數(shù)
obj
欲轉(zhuǎn)換的對象
sep
設(shè)置分隔符,默認(rèn)為 ‘&'
eq
設(shè)置賦值符排抬,默認(rèn)為 ‘='
querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// 返回 'foo=bar&baz=qux&baz=quux&corge='
8.在服務(wù)端如何區(qū)分用戶發(fā)送的是GET請求和POST請求?
通過HTTP模塊http.IncomingMessage 類的.method屬性
const http = require("http");
let server = http.createServer();
server.on("request", function (req, res) {
if (req.url !== "/favicon.ico"){
if (req.method.toLowerCase() === "get"){
console.log("GET請求");
} else if (req.method.toLowerCase() === "post"){
console.log("POST請求");
}
}
res.end();
});
server.listen(3000);
上面代碼中req.url !== "/favicon.ico"是為了過濾掉favicon請求
在第一次request請求的時(shí)候懂从,客戶端會(huì)發(fā)送一個(gè)隱式的請求給服務(wù)器,這個(gè)請求就是為了獲取到網(wǎng)頁的圖標(biāo)(就是每個(gè)網(wǎng)頁打開后Title旁邊的那個(gè)小圖標(biāo))畜埋,所以莫绣,當(dāng)我們提交表單數(shù)據(jù)的時(shí)候畴蒲,實(shí)際是觸發(fā)了兩次請求悠鞍。
9.動(dòng)態(tài)網(wǎng)站
編寫一個(gè)簡單的動(dòng)態(tài)網(wǎng)站, 實(shí)現(xiàn)用戶在地址欄輸入127.0.0.1:3000/index.html后進(jìn)入主頁, 然后再輸入框輸入姓名后跳轉(zhuǎn)到對應(yīng)姓名的詳情頁面
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="./info.html" method="post">
<input type="text" name="userName">
<input type="submit" value="查詢">
</form>
</body>
</html>
info.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li>姓名: !!!name!!!</li>
<li>性別: !!!gender!!!</li>
<li>年齡: !!!age!!!</li>
</ul>
</body>
</html>
dynamic.js
// 1.導(dǎo)入需要的模塊
const http = require("http");
const path = require("path");
const url = require("url");
const fs = require("fs");
const querystring = require("querystring");
// 4.22創(chuàng)建對象存儲(chǔ)信息
let persons = {
"lisi": {
name: "lisi",
gender: "male",
age: "33"
},
"zhangsan": {
name: "zhangsan",
gender: "female",
age: "20"
}
};
let server = http.createServer();
// 2.創(chuàng)建服務(wù)器
server.on("request", function (req, res) {
// 3.處理get請求
if (req.url.startsWith("/index") && req.method.toLowerCase() === "get"){
let obj = url.parse(req.url);
let filePath = path.join(__dirname, obj.pathname);
fs.readFile(filePath, "utf8", (err, data) => {
if (err){
res.writeHead(404, {
"Content-Type": "text/plain; charset=utf-8"
});
res.end("Page Not Found");
}
res.writeHead(200, {
"Content-Type": "text/html; charset=utf-8"
});
res.end(data);
});
}
// 4.處理post請求
else if (req.url.startsWith("/info") && req.method.toLowerCase() === "post"){
// 4.1獲取用戶請求的數(shù)據(jù)
let params = "";
req.on("data", function (chunk) {
params += chunk;
});
// 4.2獲取完成
req.on("end", function () {
// 4.21將數(shù)據(jù)轉(zhuǎn)為對象
let obj = querystring.parse(params);
// 4.23從信息對象中拿到數(shù)據(jù)
let per = persons[obj.userName];
// 4.24拼接路徑并讀取文件
let filePath = path.join(__dirname, req.url);
fs.readFile(filePath, "utf8", (err, data) => {
if (err){
res.writeHead(404, {
"Content-Type": "text/plain; charset=utf-8"
});
res.end("Page Not Found");
}
res.writeHead(200, {
"Content-Type": "text/html; charset=utf-8"
});
data = data.replace("!!!name!!!", per.name);
data = data.replace("!!!gender!!!", per.gender);
data = data.replace("!!!age!!!", per.age);
res.end(data);
});
});
}
});
server.listen(3000);
效果
我們可以發(fā)現(xiàn)上面的代碼看起來還是比較雜亂的, 我們還可以使用 art-template模板引擎優(yōu)化代碼, 下面來看看具體步驟
1.在當(dāng)前文件的目錄下輸入指令
npm init -y
初始化包, 然后我們就可以看到一個(gè)package.jaon的文件。2.根據(jù)官方提供的指令
npm install art-template --save
安裝包3.改造info.html文件, 將以前占位的符號都改為模板的形式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<!--<li>姓名: !!!name!!!</li>
<li>性別: !!!gender!!!</li>
<li>年齡: !!!age!!!</li>-->
<li>姓名: <%=name%></li>
<li>性別: <%=gender%></li>
<li>年齡: <%=age%></li>
</ul>
</body>
</html>
4.修改dynamic.js文件
4.1引入模板, 因?yàn)檫@里是第三方模塊, 所以引入的時(shí)候不需要加絕對路徑
4.2直接舍棄掉fs.readFile
方法, 改用art-template官方樣式代碼
// 1.導(dǎo)入需要的模塊
const http = require("http");
const path = require("path");
const url = require("url");
const fs = require("fs");
const querystring = require("querystring");
let template = require("art-template");
// 4.22創(chuàng)建對象存儲(chǔ)信息
let persons = {
"lisi": {
name: "lisi",
gender: "male",
age: "33"
},
"zhangsan": {
name: "zhangsan",
gender: "female",
age: "20"
}
};
let server = http.createServer();
// 2.創(chuàng)建服務(wù)器
server.on("request", function (req, res) {
// 3.處理get請求
if (req.url.startsWith("/index") && req.method.toLowerCase() === "get"){
let obj = url.parse(req.url);
let filePath = path.join(__dirname, obj.pathname);
fs.readFile(filePath, "utf8", (err, data) => {
if (err){
res.writeHead(404, {
"Content-Type": "text/plain; charset=utf-8"
});
res.end("Page Not Found");
}
res.writeHead(200, {
"Content-Type": "text/html; charset=utf-8"
});
res.end(data);
});
}
// 4.處理post請求
else if (req.url.startsWith("/info") && req.method.toLowerCase() === "post"){
// 4.1獲取用戶請求的數(shù)據(jù)
let params = "";
req.on("data", function (chunk) {
params += chunk;
});
// 4.2獲取完成
req.on("end", function () {
// 4.21將數(shù)據(jù)轉(zhuǎn)為對象
let obj = querystring.parse(params);
// 4.23從信息對象中拿到數(shù)據(jù)
let per = persons[obj.userName];
// 4.24拼接路徑并讀取文件
let filePath = path.join(__dirname, req.url);
// 使用art-template
let html = template(filePath, per);
res.writeHead(200, {
"Content-Type": "text/html; charset=utf-8"
});
res.end(html);
});
}
});
server.listen(3000);
這樣我們才算真正完成簡單的動(dòng)態(tài)網(wǎng)站