13-NodeJS核心API-http模塊

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 類型列表

如何使用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)站

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末模燥,一起剝皮案震驚了整個(gè)濱河市咖祭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔫骂,老刑警劉巖么翰,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辽旋,居然都是意外死亡浩嫌,警方通過查閱死者的電腦和手機(jī)檐迟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來码耐,“玉大人追迟,你說我怎么就攤上這事∩龋” “怎么了敦间?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長束铭。 經(jīng)常有香客問我廓块,道長,這世上最難降的妖魔是什么契沫? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任带猴,我火速辦了婚禮,結(jié)果婚禮上懈万,老公的妹妹穿的比我還像新娘浓利。我一直安慰自己,他們只是感情好钞速,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布贷掖。 她就那樣靜靜地躺著,像睡著了一般渴语。 火紅的嫁衣襯著肌膚如雪苹威。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天驾凶,我揣著相機(jī)與錄音牙甫,去河邊找鬼。 笑死调违,一個(gè)胖子當(dāng)著我的面吹牛窟哺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播技肩,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼且轨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虚婿?” 一聲冷哼從身側(cè)響起旋奢,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎然痊,沒想到半個(gè)月后至朗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剧浸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年锹引,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矗钟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫌变,死狀恐怖真仲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情初澎,我是刑警寧澤秸应,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站碑宴,受9級特大地震影響软啼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜延柠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一祸挪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贞间,春花似錦贿条、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至峻仇,卻和暖如春公黑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摄咆。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工凡蚜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吭从。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓朝蜘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涩金。 傳聞我的和親對象是個(gè)殘疾皇子谱醇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容