如何創建一個網站服務器


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

  1. 請求方式(Request Method)
  • GET 請求數據
    在瀏覽器中輸入網址使用的是get請求

  • POST 發送數據
    如何發送post請求:使用表單方式

<body>
    <!-- 
        method: 指定當前表單提交的方式
        action:指定當前表單提交的地址
     -->
    <form method="post" action="http://localhost:3000">
        <input type="submit">
    </form>
</body>

  1. 請求地址(Request URL)
    // 獲取請求地址
    // req.url

2.4 響應報文

  1. HTTP狀態
  • 200 請求成功
  • 404 請求的資源沒有被找到
  • 500 服務器端錯誤
  • 400 客戶端請求有語法錯誤

3. HTTP請求與響應處理

3.1 請求參數

客戶端向服務器發送請求時,有時需要攜帶一些卡用戶信息,客戶信息需要通過請求參數的形式傳遞服務器端,比如登錄操作。

3.2 GET請求參數

// 用於創建網站服務器的模塊
// 引用系統模塊
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關鍵字

  1. 普通函數定義前家async關鍵字 普通函數變成異步函數
  2. 異步函數默認返回promise對象
  3. 在一部函數內部使用return關鍵字進行結果返回,結果會包裹的promise對象中return關鍵字代替了resolve方法
  4. 在一部函數內部使用throw關鍵字拋出程序異常
  5. 調用異步函數再鏈式調用then方法獲取函數執行結果
  6. 調用異步函數再鏈式調用catch方法獲取異步函數執行的錯誤信息

await

  1. await關鍵字只能出現在異步函數中
  2. await promise await后面只能寫promise對象 寫其他類型的API是不不可以的
  3. 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可以省略。

    1. console.log(); 在控制台中輸出
    1. setTimeout() 設置超時定時器
    1. clearTimeout() 清除超時時定時器
    1. setInterval() 設置間歇定時器
    1. clearInterval() 清除間歇定時器


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM