1. 創建web服務器
// 創建web服務器
// 用於創建網站服務器的模塊
// 引用系統模塊
const http = require('http');
// 創建web服務器
// app對象就是網站服務器對象
const app = http.createServer();
// 當客戶端發送請求的時候
app.on('request', (req, res) => {
// 響應
res.end('<h1>hi ,user</h1>');
});
// 監聽3000端口
app.listen(3000); // 這里的端口號可以隨便寫,不一定是3000
console.log('服務器已啟動,監聽3000端口,請訪問localhost:3000');
// 在powerShall中輸入 nodemon app.js啟動服務器,再在瀏覽器中輸入localhost:3000
2. HTTP協議
2.1 http協議的概念
超文本傳輸協議(http)規定了如何從網站服務器傳輸超文本到本地瀏覽器,它是基於客戶端服務器架構工作,是客戶端(用戶)和服務器(網站)請求和應答的標准。

2.2 報文
在HTTP請求和響應的過程中傳遞的數據塊就是報文,包括要傳送的數據和一些附加信息,並且要遵守規定好的格式


2.3 請求報文
獲取請求方式:req.method
- 請求方式(Request Method)
-
GET 請求數據
在瀏覽器中輸入網址使用的是get請求

-
POST 發送數據
如何發送post請求:使用表單方式
<body>
<!--
method: 指定當前表單提交的方式
action:指定當前表單提交的地址
-->
<form method="post" action="http://localhost:3000">
<input type="submit">
</form>
</body>

- 請求地址(Request URL)
// 獲取請求地址
// req.url
2.4 響應報文
- HTTP狀態
- 200 請求成功
- 404 請求的資源沒有被找到
- 500 服務器端錯誤
- 400 客戶端請求有語法錯誤
3. HTTP請求與響應處理
3.1 請求參數
客戶端向服務器發送請求時,有時需要攜帶一些卡用戶信息,客戶信息需要通過請求參數的形式傳遞服務器端,比如登錄操作。
3.2 GET請求參數
- 參數被放置在瀏覽器地址欄中,例如:http://localhost:3000/index?name=zhangshan&&age=19
- 參數獲取需要借助系統模塊url, url 模塊用來處理url地址
// 用於創建網站服務器的模塊
// 引用系統模塊
const http = require('http');
// 用於處理url地址
const url = require('url');
// 創建web服務器
// app對象就是網站服務器對象
const app = http.createServer();
// 當客戶端發送請求的時候
app.on('request', (req, res) => {
console.log(req.url);
// url.parse的參數:1)要解析的url地址 2)將查詢參數解析成對象形式
let { query, pathname } = url.parse(req.url, true);
console.log(query.name);
console.log(query.age);
if (pathname == '/index' || pathname == '/') {
return res.end('<h2>歡迎來到首頁</h2>');
} else if (pathname == '/list') {
return res.end('welcome to listpage');
} else {
return res.end('no find');
}
});
// 監聽3000端口
app.listen(3000);
console.log('服務器已啟動,監聽3000端口,請訪問localhost:3000');
// 在powerShall中輸入 nodemon app.js啟動服務器,再在瀏覽器中輸入localhost:3000
3.3 POST請求參數
- 參數被放置在請求體中進行傳輸
- 獲取POST參數需要使用data事件和end事件
- 使用querystring系統模塊將參數轉換為對象格式
// 導入系統模塊querystring 用於將HTTP參數轉換為對象格式
const querystring = require('querystring');
app.on('request', (req, res) => {
let postData = '';
// 監聽參數傳輸事件
// post參數是通過事件的方式接受的
// data: 但請求參數傳遞的時候觸發data事件
// end:當參數傳遞完成的時候觸發end事件
req.on('data', (chunk) => postData += chunk;);
// 監聽參數傳輸完畢事件
req.on('end', () => {
console.log(querystring.parse(postData));
});
});
3.4 路由
http://localhost:3000/index 首頁
http://localhost:3000/login 登錄頁面
路由是指客戶端請求地址與服務器端程序代碼的對應關系。簡單來說,就是請求什么響應什么

// 1. 引入系統模塊http
// 2. 創建網站服務器
// 3. 為網站服務器對象添加請求事件
// 4. 實現路由功能
// 1) 獲取客戶端的請求方式
// 2) 獲取客戶端的請求地址
const http = require('http');
const url = require('url')
const app = http.createServer();
app.on('request', (req, res) => {
// 獲取請求方式
const method = req.method.toLowerCase();
// 獲取請求地址
const pathname = url.parse(req.url).pathname;
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
})
if (method == 'get') {
if (pathname == '/' || pathname == '/index') {
return res.end('歡迎來到首頁')
} else if (pathname == '/list') {
return res.end('歡迎來到列表頁');
} else {
return res.end('您訪問的頁面不存在');
}
} else if (method == 'post') {
if (pathname == '/' || pathname == '/index') {
return res.end('歡迎來到首頁')
} else if (pathname == '/list') {
return res.end('歡迎來到列表頁');
} else {
return res.end('您訪問的頁面不存在');
}
}
});
app.listen(3000);
console.log('服務器啟動成功');
3.5 靜態資源
服務器端不需要處理,可以直接響應客戶端的資源就是靜太資源,例如CSS、JavaScript、image文件。
3.6 動態資源
相同的請求地址不同的響應資源,這種資源就是動態資源。
例如:
http:www.itcast.cn/article?id=1
http:www.itcast.cn/article?id=2

const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const app = http.createServer();
app.on('request', (req, res) => {
// 獲取用戶的請求路勁
let pathname = url.parse(req.url).pathname;
pathname = pathname == '/' ? 'default.html' : pathname;
// res.writeHead(200, {
// 'content-type': 'text/html;charset=utf8'
// })
// 將用戶的請求路勁轉換為實際的服務器硬盤路勁
let realPath = path.join(__dirname, 'public' + pathname);
let type = mime.getType(realPath);
// 讀取文件
// 如果文件讀取成功 error為空,result 就是文件的內容
// 如果文件讀取失敗 error里面存儲的是失敗信息, result為空
fs.readFile(realPath, (error, result) => {
// 文件讀取失敗
if (error != null) {
res.writeHead(404, {
'content-type': 'text/html;charset=utf8'
})
res.end('文件讀取失敗');
return;
}
res.writeHead(200, {
'content-type': type
});
res.end(result);
})
});
app.listen(3000);
console.log('服務器啟動成功');
4. Node.js異步編程
4.1 同步API,異步API
同步API:只有當前API執行完成后,才能繼續執行下一個API
異步API:當前API的執行不會阻塞后續代碼的執行
console.log('before');
setTimeout(function() {
console.log('last');
}, 2000);
console.log('after');
// 輸出結果為:before after last
4.2 同步API,異步API的區別(獲取返回值)
同步API可以返回值中拿到API執行的結果,但是異步API是不可以的
// 同步
function sum(n1, n2) {
return n1 + n2;
};
console.log(sum(2, 3));
// 返回值為:5
// 異步
function getMsg() {
setTimeout(function() {
return {
msg: 'hello node.js'
}
}, 2000);
// 由於在執行getMsg時,遇到定時器所以直接跳到定時器的后面,添加return undefined執行,將其值返回給msg
// return undefined
}
const msg = getMsg();
console.log(msg);
// 返回值為undefined
4.3 回調函數
自己定義函數讓別人去調用
// 異步
function getMsg(callback) {
setTimeout(function() {
callback({
msg: 'hello node.js'
});
}, 2000);
}
getMsg(function(date) {
console.log(date);
});
4.4 同步API,異步API的區別(代碼執行順序)
同步API從上到下依次執行,前面代碼會阻塞后面代碼的執行
for (var i = 0; i < 10000; i++) {
console.log(i);
}
console.log('執行后面的代碼');
異步API不會等待API執行完成后再向下執行代碼
console.log('代碼開始執行');
setTimeout(() => {
console.log('2秒后執行的代碼');
}, 2000);
setTimeout(() => {
console.log('0秒后執行的代碼');
}, 0);
console.log('代碼結束執行');
// 代碼的執行結果
// 代碼開始執行
// 代碼結束執行
// 0秒后執行的代碼
// 2秒后執行的代碼
4.5 代碼執行順序分析

console.log('代碼開始執行');
setTimeout(() => {
console.log('2秒后執行的代碼');
}, 2000);
setTimeout(() => {
console.log('"0秒"后執行的代碼');
}, 0);
console.log('代碼結束執行');
4.6 Node.js中的異步API
const fs = require('fs');
fs.readFile('1.txt', 'utf-8', (err, result1) => {
console.log(result1);
fs.readFile('2.txt', 'utf-8', (err, result2) => {
console.log(result2);
fs.readFile('3.txt', 'utf-8', (err, result3) => {
console.log(result3);
})
})
})
如果異步API后面代碼的執行依賴當前異步API的執行結果,但實際上后續代碼在執行的時候異步API還沒有返回結果,這個問題怎么解決呢?
const fs = require('fs');
let promise = new Promise((resolve, reject) => {
fs.readFile('100.txt', 'utf8', (err, result) => {
if (err != null) {
return reject(err);
} else {
return resolve(result);
}
});
});
promise.then((result) => {
console.log(result);
})
.catch((err) => {
console.log(err);
})
4.7 Promise
Promise出現的目的是解決Node.js異步編程中回調地獄的問題。

4.8 異步函數
異步函數是異步編程語法的終極解決方案,它可以讓我們將異步代碼寫成同步的形式,讓代碼不再有回調函數嵌套,使代碼變得清晰明了。
const fn = async () => {}
async function fn() {}
async關鍵字
- 普通函數定義前家async關鍵字 普通函數變成異步函數
- 異步函數默認返回promise對象
- 在一部函數內部使用return關鍵字進行結果返回,結果會包裹的promise對象中return關鍵字代替了resolve方法
- 在一部函數內部使用throw關鍵字拋出程序異常
- 調用異步函數再鏈式調用then方法獲取函數執行結果
- 調用異步函數再鏈式調用catch方法獲取異步函數執行的錯誤信息
await
- await關鍵字只能出現在異步函數中
- await promise await后面只能寫promise對象 寫其他類型的API是不不可以的
- await關鍵字可是暫停異步函數向下執行 直到promise返回結果
// 1. 在普通函數定義的前面加上async關鍵字 普通函數就變成了異步函數
// 2. 異步函數默認的返回值使promise對象
// 3. 在異步函數內保護使用throw關鍵字進行錯誤拋出
// 4. await關鍵字
// 它只能出現在異步函數中
// awite后面跟promise對象 它可以暫停異步函數的執行,等待promise對象返回后再向下執行函數
// async function fn() {
// throw '發生了一些錯誤';
// return 123;
// }
// // console.log(fn());
// fn().then(function(data) {
// console.log(data);
// })
// .catch(function(err) {
// console.log(err);
// })
async function p1() {
return 'p1';
}
async function p2() {
return 'p2';
}
async function p3() {
return 'p3';
}
async function run() {
let r1 = await p1();
let r2 = await p2();
let r3 = await p3();
console.log(r1);
console.log(r2);
console.log(r3);
}
run();
const fs = require('fs');
const promisify = require('util').promisify;
const readFile = promisify(fs.readFile);
async function run() {
let r1 = await readFile('1.txt', 'utf8');
let r2 = await readFile('2.txt', 'utf8');
let r3 = await readFile('3.txt', 'utf8');
console.log(r1);
console.log(r2);
console.log(r3);
}
run();
4.9 Node.js全局對象global
在瀏覽器中全局對象是window, 在Node中全局對象是global。
Node中全局對象有以下方法, 在任何地方使用, global可以省略。
-
- console.log(); 在控制台中輸出
-
- setTimeout() 設置超時定時器
-
- clearTimeout() 清除超時時定時器
-
- setInterval() 設置間歇定時器
-
- clearInterval() 清除間歇定時器
