一、 服務器端基礎概念
1 網站的組成
網站應用程序主要分爲兩大部分:客戶端和服務器端。
客戶端:在瀏覽器中運行的部分,就是用戶看到並與之交互的界面程序。使用HTML、CSS、JavaScript構建。
服務器端:在服務器中運行的部分,負責存儲數據和處理應用邏輯。
2 Node網站服務器
能夠提供網站訪問服務的機器就是網站服務器,它能夠接收客戶端的 請求 ,能夠對請求做出 響應 。
3 IP地址
互聯網中設備的唯一標識。
IP是Internet Protocol Address的簡寫,代表互聯網協議地址.
4 域名
由於IP地址難於記憶,所以產生了域名的概念,所謂域名就是平時 上網所使用的網址 。
http://www.baidu.com => http://119.75.217.109/
雖然在地址欄中輸入的是網址, 但是最終還是會將域名轉換爲ip才能訪問到指定的網站服務器。
5 端口
端口是計算機與外界通訊交流的出口,用來區分服務器電腦中提供的不同的服務。
6 URL
統一資源定位符,又叫URL(Uniform Resource Locator),是專爲標識Internet網上資源位置而設的一種編址方式,我們平時所說的網頁地址指的即是URL。
URL的組成
傳輸協議😕/服務器IP或域名:端口/資源所在位置標識
https://www.baidu.com/s?wd=node
http:超文本傳輸協議,提供了一種發佈和接收HTML頁面的方法。
7 開發過程中客戶端和服務器端說明
在開發階段,客戶端和服務器端使用同一臺電腦,即開發人員電腦。
二、 創建web服務器
創建web服務器
// 引用系統模塊
const http = require('http');
// 創建web服務器
const app = http.createServer();
// 當客戶端發送請求的時候
app.on('request', (req, res) => {
// 響應
res.end('<h1>hi, user</h1>');
});
// 監聽3000端口
app.listen(3000);
console.log('服務器已啓動,監聽3000端口,請訪問 localhost:3000')
三、 HTTP協議
1 HTTP協議的概念
超文本傳輸協議(英文:HyperText Transfer Protocol,縮寫:HTTP)規定了如何從網站服務器傳輸超文本到本地瀏覽器,它基於客戶端服務器架構工作,是客戶端(用戶)和服務器端(網站)請求和應答的標準。
2 報文
在HTTP請求和響應的過程中傳遞的數據塊就叫報文,包括要傳送的數據和一些附加信息,並且要遵守規定好的格式。
3 請求報文
① 請求方式 (Request Method)
- GET 請求數據
- POST 發送數據
② 請求地址 (Request URL)
app.on('request', (req, res) => {
req.headers // 獲取請求報文
req.url // 獲取請求地址
req.method // 獲取請求方法
});
4 響應報文
① HTTP狀態碼
- 200 請求成功
- 404 請求的資源沒有被找到
- 500 服務器端錯誤
- 400 客戶端請求有語法錯誤
② 內容類型 - text/html
- text/css
- application/javascript
- image/jpeg
- application/json
四、HTTP請求與響應處理
1 請求參數
客戶端向服務器端發送請求時,有時需要攜帶一些客戶信息,客戶信息需要通過請求參數的形式傳遞到服務器端,比如登錄操作。
2 GET請求參數
- 參數被放置在瀏覽器地址欄中,例如:http://localhost:3000/?name=zhangsan&age=20
- 參數獲取需要藉助系統模塊url,url模塊用來處理url地址
const http = require('http');
// 導入url系統模塊 用於處理url地址
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
// 將url路徑的各個部分解析出來並返回對象
// true 代表將參數解析爲對象格式
let {query} = url.parse(req.url, true);
console.log(query);
});
app.listen(3000);
3 POST請求參數
- 參數被放置在請求體中進行傳輸
- 獲取POST參數需要使用data事件和end事件
- 使用querystring系統模塊將參數轉換爲對象格式
// 導入系統模塊querystring 用於將HTTP參數轉換爲對象格式
const querystring = require('querystring');
app.on('request', (req, res) => {
let postData = '';
// 監聽參數傳輸事件
req.on('data', (chunk) => postData += chunk;);
// 監聽參數傳輸完畢事件
req.on('end', () => {
console.log(querystring.parse(postData));
});
});
4 路由
http://localhost:3000/index
http://localhost:3000/login
路由是指客戶端請求地址與服務器端程序代碼的對應關係。簡單的說,就是請求什麼響應什麼。
// 當客戶端發來請求的時候
app.on('request', (req, res) => {
// 獲取客戶端的請求路徑
let { pathname } = url.parse(req.url);
if (pathname == '/' || pathname == '/index') {
res.end('歡迎來到首頁');
} else if (pathname == '/list') {
res.end('歡迎來到列表頁頁');
} else {
res.end('抱歉, 您訪問的頁面出遊了');
}
});
5 靜態資源
服務器端不需要處理,可以直接響應給客戶端的資源就是靜態資源,例如CSS、JavaScript、image文件。
http://www.xxNode.cn/images/logo.png
6 動態資源
相同的請求地址不同的響應資源,這種資源就是動態資源。
http://www.xxnode.cn/article?id=1
http://www.xxnode.cn/article?id=2
7 客戶端請求途徑
① GET方式
- 瀏覽器地址欄
- link標籤的href屬性
- script標籤的src屬性
- img標籤的src屬性
- Form表單提交
② POST方式 - Form表單提交
五、Node.js異步編程
// 路徑拼接
const public = path.join(__dirname, 'public');
// 請求地址解析
const urlObj = url.parse(req.url);
// 讀取文件
fs.readFile('./demo.txt', 'utf8', (err, result) => {
console.log(result);
});
1 同步 API,異步 API
同步API:只有當前API執行完成後,才能繼續執行下一個API
console.log('before');
console.log('after');
異步API:當前API的執行不會阻塞後續代碼的執行
console.log('before');
setTimeout(
() => { console.log('last');
}, 2000);
console.log('after');
2 同步API, 異步API的區別( 獲取返回值 )
同步API可以從返回值中拿到API執行的結果, 但是異步API是不可以的
// 同步
function sum (n1, n2) {
return n1 + n2;
}
const result = sum (10, 20);
// 異步
function getMsg () {
setTimeout(function () {
return { msg: 'Hello Node.js' }
}, 2000);
}
const msg = getMsg ();
3 回調函數
自己定義函數讓別人去調用。
// getData函數定義
function getData (callback) {}
// getData函數調用
getData (() => {});
4 使用回調函數獲取異步API執行結果
function getMsg (callback) {
setTimeout(function () {
callback ({ msg: 'Hello Node.js' })
}, 2000);
}
getMsg (function (msg) {
console.log(msg);
});
5 同步API, 異步API的區別(代碼執行順序)
同步API從上到下依次執行,前面代碼會阻塞後面代碼的執行
for (var i = 0; i < 100000; i++) {
console.log(i);
}
console.log('for循環後面的代碼');
異步API不會等待API執行完成後再向下執行代碼
console.log('代碼開始執行');
setTimeout(() => { console.log('2秒後執行的代碼')}, 2000);
setTimeout(() => { console.log('"0秒"後執行的代碼')}, 0);
console.log('代碼結束執行');
6 代碼執行順序分析
console.log('代碼開始執行');
setTimeout(() => {
console.log('2秒後執行的代碼');
}, 2000);
setTimeout(() => {
console.log('"0秒"後執行的代碼');
}, 0);
console.log('代碼結束執行');
7 Node.js中的異步API
fs.readFile('./demo.txt', (err, result) => {});
var server = http.createServer();
server.on('request', (req, res) => {});
如果異步API後面代碼的執行依賴當前異步API的執行結果,但實際上後續代碼在執行的時候異步API還沒有返回結果,這個問題要怎麼解決呢?
fs.readFile('./demo.txt', (err, result) => {});
console.log('文件讀取結果');
需求:依次讀取A文件、B文件、C文件
8 Promise
Promise出現的目的是解決Node.js異步編程中回調地獄的問題。
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (true) {
resolve({name: '張三'})
}else {
reject('失敗了')
}
}, 2000);
});
promise.then(result => console.log(result); // {name: '張三'})
.catch(error => console.log(error); // 失敗了)
9 異步函數
異步函數是異步編程語法的終極解決方案,它可以讓我們將異步代碼寫成同步的形式,讓代碼不再有回調函數嵌套,使代碼變得清晰明瞭。
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返回結果