04-Node.js-HTTP 協(xié)議

概念

HTTP(hypertext transport protocol)協(xié)議;中文叫超文本傳輸協(xié)議

是一種基于TCP/IP的應(yīng)用層通信協(xié)議
這個(gè)協(xié)議詳細(xì)規(guī)定了 瀏覽器 和 萬(wàn)維網(wǎng) 服務(wù)器 之間互相通信的規(guī)則

協(xié)議中主要規(guī)定了兩個(gè)方面的內(nèi)容:

  • 客戶端:用來(lái)向服務(wù)器發(fā)送數(shù)據(jù),可以被稱之為請(qǐng)求報(bào)文
  • 服務(wù)端:向客戶端返回?cái)?shù)據(jù),可以被稱之為響應(yīng)報(bào)文

報(bào)文:可以簡(jiǎn)單理解為就是一堆字符串

請(qǐng)求報(bào)文的組成

  • 請(qǐng)求行
  • 請(qǐng)求頭
  • 空行
  • 請(qǐng)求體

HTTP 的請(qǐng)求行

  • 請(qǐng)求方式(get、post、put、delete等)
  • 請(qǐng)求 URL(統(tǒng)一資源定位器)
    • http: 協(xié)議 (https、ftp、ssh等)
    • www.baidu.com 域名
    • 80 端口號(hào)
    • /index.html 路徑
    • a=100&b=200 查詢字符串
    • logo 哈希 (錨點(diǎn)鏈接)
  • HTTP協(xié)議版本號(hào)

HTTP 請(qǐng)求頭

格式:『頭名:頭值』
常見(jiàn)的請(qǐng)求頭有:

請(qǐng)求頭 解釋
Host 主機(jī)名
Connection 連接的設(shè)置 keep-alive(保持連接);close(關(guān)閉連接)
Cache-Control 緩存控制 max-age = 0 (沒(méi)有緩存)
Upgrade-Insecure-Requests 將網(wǎng)頁(yè)中的http請(qǐng)求轉(zhuǎn)化為 https 請(qǐng)求(很少用)老網(wǎng)站升級(jí)
User-Agent 用戶代理,客戶端字符串標(biāo)識(shí),服務(wù)器可以通過(guò)這個(gè)標(biāo)識(shí)來(lái)識(shí)別這個(gè)請(qǐng)求來(lái)自哪個(gè)客戶端 ,一般在PC端和手機(jī)端的區(qū)分
Accept 設(shè)置瀏覽器接收的數(shù)據(jù)類型
Accept-Encoding 設(shè)置接收的壓縮方式
Accept-Language 設(shè)置接收的語(yǔ)言 q=0.7 為喜好系數(shù),滿分為1
Cookie 后面單獨(dú)講

HTTP 的請(qǐng)求體

請(qǐng)求體內(nèi)容的格式是非常靈活的,

(可以是空)==> GET請(qǐng)求,

(也可以是字符串,還可以是JSON)===> POST請(qǐng)求

例如:

  • 字符串:keywords=手機(jī)&price=2000

  • JSON:{"keywords":"手機(jī)","price":2000}

響應(yīng)報(bào)文的組成

  • 響應(yīng)行: HTTP/1.1 200 OK

響應(yīng)狀態(tài)碼 和 響應(yīng)字符串 關(guān)系是 一一對(duì)應(yīng) 的。

  • 響應(yīng)頭

    • Cache-Control: 緩存控制 private 私有的,只允許客戶端緩存數(shù)據(jù)
    • Connection: 鏈接設(shè)置
    • Content-Type:text/html;charset=utf-8: 設(shè)置響應(yīng)體的數(shù)據(jù)類型以及字符集,響應(yīng)體為html,字符集utf-8
    • Content-Length: 響應(yīng)體的長(zhǎng)度,單位為字節(jié)
  • 空行

  • 響應(yīng)體

    響應(yīng)體內(nèi)容的類型是非常靈活的,常見(jiàn)的類型有 HTML、CSS、JS、圖片、JSON

創(chuàng)建 HTTP 服務(wù)

使用 nodejs 創(chuàng)建 HTTP 服務(wù)

操作步驟

//1. 導(dǎo)入 http 模塊
const http = require('http');


//2. 創(chuàng)建服務(wù)對(duì)象 create 創(chuàng)建 server 服務(wù)
// request 意為請(qǐng)求. 是對(duì)請(qǐng)求報(bào)文的封裝對(duì)象, 通過(guò) request 對(duì)象可以獲得請(qǐng)求報(bào)文的數(shù)據(jù)
// response 意為響應(yīng). 是對(duì)響應(yīng)報(bào)文的封裝對(duì)象, 通過(guò) response 對(duì)象可以設(shè)置響應(yīng)報(bào)文
const server = http.createServer((request, response) => {       
    // 設(shè)置響應(yīng)體
    response.end('Hello HTTP server');
});  //=>返回結(jié)果是一個(gè)對(duì)象

//3. 監(jiān)聽(tīng)端口, 啟動(dòng)服務(wù)
server.listen(9000, () => {
    console.log('服務(wù)已經(jīng)啟動(dòng), 端口 9000 監(jiān)聽(tīng)中...');
});

http.createServer 里的回調(diào)函數(shù)的執(zhí)行時(shí)機(jī): <span style="color:red">當(dāng)接收到 HTTP 請(qǐng)求的時(shí)候,就會(huì)執(zhí)行</span>

測(cè)試

瀏覽器請(qǐng)求對(duì)應(yīng)端口

http://127.0.0.1:9000

注意事項(xiàng)

  1. 命令行 ctrl + c 停止服務(wù)

  2. 當(dāng)服務(wù)啟動(dòng)后,更新代碼必須重啟服務(wù)才能生效

  3. 響應(yīng)內(nèi)容中文亂碼的解決辦法

    // 設(shè)置響應(yīng)頭
    response.setHeader('content-type','text/html;charset=utf-8');
    
  4. 端口號(hào)被占用

    Error: listen EADDRINUSE: address already in use :::9000

    1)關(guān)閉當(dāng)前正在運(yùn)行監(jiān)聽(tīng)端口的服務(wù) ( 使用較多 )

    2)修改其他端口號(hào)

  5. HTTP 協(xié)議默認(rèn)端口是 80 。HTTPS 協(xié)議的默認(rèn)端口是 443, HTTP 服務(wù)開(kāi)發(fā)常用端口有 3000,8080,8090,9000

如果端口被其他程序占用,可以使用資源監(jiān)視器找到占用端口的程序,然后使用任務(wù)管理器關(guān)閉對(duì)應(yīng)的程序

瀏覽器查看 HTTP 報(bào)文

點(diǎn)擊步驟

查看請(qǐng)求行與請(qǐng)求頭

查看請(qǐng)求體

查看 URL 查詢字符串

查看響應(yīng)行與響應(yīng)頭

查看響應(yīng)體

獲取 HTTP 請(qǐng)求報(bào)文

含義 語(yǔ)法 重點(diǎn)掌握
請(qǐng)求方法 request.method *****
請(qǐng)求版本 request.httpVersion
請(qǐng)求路徑 request.url *****
URL 路徑 require('url').parse(request.url).pathname *****
URL 查詢字符串 require('url').parse(request.url, true).query *****
請(qǐng)求頭 request.headers *****
請(qǐng)求體 request.on('data', function(chunk){})
request.on('end', function(){})
// 1. 導(dǎo)入 http 模塊
const http = require('http')

// 2. 創(chuàng)建服務(wù)對(duì)象
const server = http.createServer((request, response) => {
  // 獲取請(qǐng)求的方法
  console.log(request.method)  //=>GET
  // 獲取請(qǐng)求的 url
  console.log(request.url)  // 只包含 url 中的 路徑 與查詢字符串
  // 獲取 http 協(xié)議的版本號(hào)
  console.log(request.httpVersion)  //=> 1.1
  // 獲取 http 的請(qǐng)求頭
  console.log(request.headers) //=>結(jié)果是一個(gè)對(duì)象
  response.end('http') //=>設(shè)置響應(yīng)體
})

// 3. 監(jiān)聽(tīng)端口,啟動(dòng)服務(wù)
server.listen(9000, () => {
  console.log('服務(wù)已經(jīng)啟動(dòng)...')
})

注意事項(xiàng):

  1. request.url 只能獲取路徑以及查詢字符串,無(wú)法獲取 URL 中的域名以及協(xié)議的內(nèi)容
  2. request.headers 將請(qǐng)求信息轉(zhuǎn)化成一個(gè)對(duì)象,并將屬性名都轉(zhuǎn)化成了『小寫(xiě)』
  3. 關(guān)于路徑:如果訪問(wèn)網(wǎng)站的時(shí)候,只填寫(xiě)了 IP 地址或者是域名信息,此時(shí)請(qǐng)求的路徑為『 /
  4. 關(guān)于 favicon.ico:這個(gè)請(qǐng)求是屬于瀏覽器自動(dòng)發(fā)送的請(qǐng)求

提取 http 報(bào)文的請(qǐng)求體

// 1. 導(dǎo)入 http 模塊
const http = require('http')

// 2. 創(chuàng)建服務(wù)對(duì)象
const server = http.createServer((request, response) => {
  // 1. 聲明一個(gè)變量
  let body = ''
  // 2. 綁定 data 事件
  request.on('data', chunk => {
    body += chunk
  })
  // 3. 綁定 end 事件
  request.on('end', () => {
    console.log(body)  //=>'username=111&password=111'
    // 響應(yīng)
    response.end('Hello Http') //=>設(shè)置響應(yīng)體 
  })
})

// 3. 監(jiān)聽(tīng)端口,啟動(dòng)服務(wù)
server.listen(9000, () => {
  console.log('服務(wù)已經(jīng)啟動(dòng)...')
})

提取 http 報(bào)文中 url的路徑 與 查詢字符串

// 導(dǎo)入 http 模塊
const http = require('http')
// 1. 導(dǎo)入 url 模塊
const url = require('url')

// 創(chuàng)建服務(wù)對(duì)象
const server = http.createServer((request, response) => {
  // 2. 解析 request.url
  console.log(request.url)   //=>/search?keyword=h5
  // 使用 parse 解析 request.url 的內(nèi)容
  // true 將 query 屬性將會(huì)設(shè)置為一個(gè) 對(duì)象
  let res = url.parse(request.url, true)
  console.log(res)  // 如下圖所示,為一個(gè)對(duì)象
  // 路徑
  let pathname = res.pathname
  // 查詢字符串
  let keyword = res.query.keyword
  console.log(keyword)   //=>h5
  response.end('url')
})

// 監(jiān)聽(tīng)端口,啟動(dòng)服務(wù)
server.listen(9000, () => {
  console.log('服務(wù)已經(jīng)啟動(dòng)...')
})
// 導(dǎo)入 http 模塊
const http = require('http')

// 創(chuàng)建服務(wù)對(duì)象
const server = http.createServer((request, response) => {
  // 實(shí)例化 url 對(duì)象
  // let url = new URL('/search?a=100&b=200','http://127.0.0.1:9000')
  let url = new URL(request.url, 'http://127.0.0.1')
  console.log(url)  //=>如圖所示,為一個(gè)對(duì)象
  // 輸出路徑
  console.log(url.pathname)  //=>/search
  // 輸出 keyword 查詢字符串
  console.log(url.searchParams.get('a'))  //=> 100
  response.end('url new')
})

// 監(jiān)聽(tīng)端口,啟動(dòng)服務(wù)
server.listen(9000, () => {
  console.log('服務(wù)已經(jīng)啟動(dòng)...')
})

練習(xí)

按照以下要求搭建 HTTP 服務(wù)

請(qǐng)求類型(方法) 請(qǐng)求地址 響應(yīng)體結(jié)果
get /login 登錄頁(yè)面
get /reg 注冊(cè)頁(yè)面
//1、引入http模塊
const http = require("http");

//2、建立服務(wù)
const server = http.createServer((request,response)=>{ 
    let {url, method} = request; //對(duì)象的解構(gòu)賦值

    //設(shè)置響應(yīng)頭信息
    //解決中文亂碼
    response.setHeader("Content-Type","text/html;charset=utf-8") 
    if(url == "/register" && method == "GET"){
        response.end("注冊(cè)頁(yè)面");
    }else if(url=="/login" && method == "GET"){
        response.end("登錄頁(yè)面");
    }else{
        response.end("<h1>404 Not Found</h1>")
    }
})

//3、監(jiān)聽(tīng)端口
server.listen(8000,()=>{
    console.log('服務(wù)啟動(dòng)中....');
})

設(shè)置 HTTP 響應(yīng)報(bào)文

作用 語(yǔ)法
設(shè)置響應(yīng)狀態(tài)碼 response.statusCode
設(shè)置響應(yīng)狀態(tài)描述 response.statusMessage ( 用的非常少 )
設(shè)置響應(yīng)頭信息 response.setHeader('頭名', '頭值') (可以自定義)
設(shè)置響應(yīng)體 response.write('xx')
response.end('xxx')只能用1次
// 1. 設(shè)置響應(yīng)狀態(tài)碼
response.statusCode = 203
// 2. 響應(yīng)狀態(tài)的描述
response.statusMessage = 'i love you'
// 3. 響應(yīng)頭
response.setHeader('content-type', 'text/html;charset=utf-8')
// 自定義響應(yīng)頭
response.setHeader('myHeader', 'test test')
// 設(shè)置多個(gè)同名的響應(yīng)頭
response.setHeader('test', ['a', 'b', 'c'])
// write 和 end 的兩種使用情況:
// 1. write 和 end 的結(jié)合使用 響應(yīng)體相對(duì)分散
response.write('xx');
response.write('xx');
response.write('xx');
response.end(); //每一個(gè)請(qǐng)求,在處理的時(shí)候必須要執(zhí)行 end 方法的

//2. 單獨(dú)使用 end 方法 響應(yīng)體相對(duì)集中
response.end('xxx');

練習(xí)

搭建 HTTP 服務(wù),響應(yīng)一個(gè) 4 行 3 列的表格,并且要求表格有 隔行換色效果 ,且 點(diǎn)擊 單元格能 高亮顯示

方法一

// 導(dǎo)入 http 模塊
const http = require('http')
const fs = require('fs')

// 創(chuàng)建服務(wù)對(duì)象
const server = http.createServer((request, response) => {
  response.setHeader('content-type', 'text/html;charset=utf-8')
  response.end(`      //這里為了能換行使用反引號(hào),英文狀態(tài)下1左邊的鍵
    <!DOCTYPE html>
    <html lang="en">

    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
        td {
          padding: 20px 40px;
        }

        table tr:nth-child(odd) {
          background-color: #aef;
        }

        table tr:nth-child(even) {
          background-color: #fcb;
        }

        table,
        td {
          border-collapse: collapse;
        }
      </style>
    </head>

    <body>
      <table border="1">
        <tr>
          <td></td>
          <td></td>
          <td></td>
        </tr>
        <tr>
          <td></td>
          <td></td>
          <td></td>
        </tr>
        <tr>
          <td></td>
          <td></td>
          <td></td>
        </tr>
        <tr>
          <td></td>
          <td></td>
          <td></td>
        </tr>
      </table>
      <script>
        const tds = document.querySelectorAll('td')
        tds.forEach(item => {
          item.addEventListener('click', function () {
            this.style.backgroundColor = '#000'
          })
        })
      </script>
    </body>

    </html>
  `)
})

// 監(jiān)聽(tīng)端口,啟動(dòng)服務(wù)器
server.listen(9000, () => {
  console.log('服務(wù)器已經(jīng)啟動(dòng)...')
})

方法二

// 導(dǎo)入 http 模塊
const http = require('http')
const fs = require('fs')

// 創(chuàng)建服務(wù)對(duì)象
const server = http.createServer((request, response) => {
  response.setHeader('content-type', 'text/html;charset=utf-8')
  // 讀取文件內(nèi)容
  let html = fs.readFileSync(__dirname + '/table.html')
  // end 方法的參數(shù)可以是字符串也可以是Buffer
  response.end(html)
})

// 監(jiān)聽(tīng)端口,啟動(dòng)服務(wù)器
server.listen(9000, () => {
  console.log('服務(wù)器已經(jīng)啟動(dòng)...')
})

table.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    td {
      padding: 20px 40px;
    }

    table tr:nth-child(odd) {
      background-color: #aef;
    }

    table tr:nth-child(even) {
      background-color: #fcb;
    }

    table,
    td {
      border-collapse: collapse;
    }
  </style>
</head>

<body>
  <table border="1">
    <tr>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
    </tr>
  </table>
  <script>
    const tds = document.querySelectorAll('td')
    tds.forEach(item => {
      item.addEventListener('click', function () {
        this.style.backgroundColor = '#000'
      })
    })
  </script>
</body>

</html>

網(wǎng)頁(yè)資源的基本加載過(guò)程

網(wǎng)頁(yè)資源的加載都是循序漸進(jìn)的,首先獲取 HTML 的內(nèi)容, 然后解析 HTML 在發(fā)送其他資源的請(qǐng)求,如 CSS,Javascript,圖片等。>理解了這個(gè)內(nèi)容對(duì)于后續(xù)的學(xué)習(xí)與成長(zhǎng)有非常大的幫助

靜態(tài)資源服務(wù)

靜態(tài)資源 是指內(nèi)容長(zhǎng)時(shí)間不發(fā)生改變的資源 ,例如圖片,視頻,CSS 文件,JS文件,HTML文件,字體文件等

動(dòng)態(tài)資源 是指內(nèi)容經(jīng)常更新的資源 ,例如百度首頁(yè),網(wǎng)易首頁(yè),京東搜索列表頁(yè)面等

網(wǎng)站根目錄或靜態(tài)資源目錄

HTTP 服務(wù)在哪個(gè)文件夾中尋找靜態(tài)資源,那個(gè)文件夾就是靜態(tài)資源目錄 ,也稱之為網(wǎng)站根目錄

思考:vscode 中使用 live-server 訪問(wèn) HTML 時(shí), 它啟動(dòng)的服務(wù)中網(wǎng)站根目錄是誰(shuí)?

  • 改文件的所處的文件夾

網(wǎng)頁(yè)中的 URL

網(wǎng)頁(yè)中的 URL 主要分為兩大類:相對(duì)路徑絕對(duì)路徑

絕對(duì)路徑

絕對(duì)路徑可靠性強(qiáng),而且相對(duì)容易理解,在項(xiàng)目中運(yùn)用較多

形式 特點(diǎn)
http://atguigu.com/web 直接向目標(biāo)資源發(fā)送請(qǐng)求,容易理解。網(wǎng)站的外鏈會(huì)用到此形式
//atguigu.com/web 與頁(yè)面 URL 的協(xié)議拼接形成完整 URL 再發(fā)送請(qǐng)求。大型網(wǎng)站用的比較多
/web 與頁(yè)面 URL 的協(xié)議、主機(jī)名、端口拼接形成完整 URL 再發(fā)送請(qǐng)求。中小型網(wǎng)站

相對(duì)路徑

相對(duì)路徑在發(fā)送請(qǐng)求時(shí),需要與當(dāng)前頁(yè)面 URL 路徑進(jìn)行 計(jì)算 ,得到完整 URL 后,再發(fā)送請(qǐng)求,學(xué)習(xí)階段用的較多

例如當(dāng)前網(wǎng)頁(yè) url 為 http://www.atguigu.com/course/h5.html

形式 最終的 URL
./css/app.css http://www.atguigu.com/course/css/app.css
js/app.js http://www.atguigu.com/course/js/app.js
../img/logo.png http://www.atguigu.com/img/logo.png
../../mp4/show.mp4 http://www.atguigu.com/mp4/show.mp4

網(wǎng)頁(yè)中使用 URL 的場(chǎng)景小結(jié)

包括但不限于如下場(chǎng)景:

  • a 標(biāo)簽 href
  • link 標(biāo)簽 href
  • script 標(biāo)簽 src
  • img 標(biāo)簽 src
  • video audio 標(biāo)簽 src
  • form 中的 action
  • AJAX 請(qǐng)求中的 URL

設(shè)置資源類型(mime類型)

媒體類型(通常稱為 Multipurpose Internet Mail Extensions 或 MIME 類型 )是一種標(biāo)準(zhǔn),用來(lái)表示文檔、文件或字節(jié)流的性質(zhì)和格式。

mime 類型結(jié)構(gòu): [type]/[subType]

例如: text/html text/css image/jpeg image/png application/json

HTTP 服務(wù)可以設(shè)置響應(yīng)頭 Content-Type 來(lái)表明響應(yīng)體的 MIME 類型,瀏覽器會(huì)根據(jù)該類型決定如何處理資源

下面是常見(jiàn)文件對(duì)應(yīng)的 mime 類型

html: 'text/html',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg', 
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json'

對(duì)于未知的資源類型,可以選擇 application/octet-stream 類型,瀏覽器在遇到該類型的響應(yīng)時(shí),會(huì)對(duì)響應(yīng)體內(nèi)容進(jìn)行獨(dú)立存儲(chǔ),也就是我們常見(jiàn)的 下載 效果

中文亂碼問(wèn)題:
①響應(yīng)頭mime類型后面指定編碼集:content-type: text/html; charset=UTF-8
②在主頁(yè)面HTML文件里面加上meta標(biāo)簽指定編碼集:<meta charset=UTF-8" />
③優(yōu)先級(jí):響應(yīng)頭>頁(yè)面 (一般不會(huì)設(shè)置不同)
④在頁(yè)面指定字符集后,頁(yè)面中加載的css、js、圖片等資源時(shí)可以不設(shè)置字符集默認(rèn)用主頁(yè)面字符集加載到頁(yè)面

GET POST 請(qǐng)求場(chǎng)景小結(jié)

GET 請(qǐng)求的情況:

  • 在地址欄直接輸入 url 訪問(wèn)
  • 點(diǎn)擊 a 鏈接
  • link 標(biāo)簽引入 css
  • script 標(biāo)簽引入 js
  • img 標(biāo)簽引入圖片
  • form 標(biāo)簽中的 method 為 get (不區(qū)分大小寫(xiě))
  • ajax 中的 get 請(qǐng)求

POST 請(qǐng)求的情況:

  • form 標(biāo)簽中的 method 為 post(不區(qū)分大小寫(xiě))

  • AJAX 的 post 請(qǐng)求

GETPOST請(qǐng)求的區(qū)別

GETPOST 是 HTTP 協(xié)議請(qǐng)求的兩種方式。

  • GET 主要用來(lái)獲取數(shù)據(jù),POST 主要用來(lái)提交數(shù)據(jù)
  • GET 帶參數(shù)請(qǐng)求是將參數(shù)綴到 URL 之后,在地址欄中輸入 url 訪問(wèn)網(wǎng)站就是 GET 請(qǐng)求,POST 帶參數(shù)請(qǐng)求是將參數(shù)放到請(qǐng)求體中
  • POST 請(qǐng)求相對(duì) GET 安全一些,因?yàn)樵跒g覽器中參數(shù)會(huì)暴露在地址欄
  • GET 請(qǐng)求大小有限制,一般為 2K,而 POST 請(qǐng)求則沒(méi)有
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容