瀏覽器中的網絡請求

瀏覽器中的網絡請求

第一次寫於: 2020年3月29日

最近, 學習了一些網絡知識; 所以就想着結合實際用到的內容, 寫一篇文章;

ajax

我們使用HTTP協議用於和server端的數據交互;
其中最常用的方式就是 ajax

它的定義和好處無需多言; 網上很多, 不再贅述;
有很多文章講的都是框架之上的使用, 屏蔽了原生內容;
我就想着稍微地瞭解一些具體瀏覽器是怎麼操作的;
這裏給大家做一個分享;

ajax 其實是對 HTTP 協議的封裝;
它會發起一個HTTP請求, 在請求的狀態改變(readyState)時, 執行我們傳入的方法;

發起請求

ajax 在瀏覽器中通過內置的 XMLHttpRequest 來生成;

let httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
        // Everything is good, the response was received.
    } else {
        // Not ready yet.
    }
};
// 請求方法 請求URL 是否異步
httpRequest.open('GET', 'http://www.example.org/some.file', true);
// 在send之前; open之後
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send("name=value&anothername=" + encodeURIComponent('哈哈哈') + "&so=on");

我們可以看到代碼中的操作

  1. 打開一個請求 指定請求方法,請求URL,是否異步
  2. 設置請求頭 這一步需要在 在send之前,open之後
  3. 發送數據 數據可以是字符串也可以是二進制數據

請求參數編碼

GET

httpRequest.send("name=value&anothername=" + encodeURIComponent('哈哈哈') + "&so=on");

我們可以注意到這行代碼中 encodeURIComponent 方法;
我們應該對參數名和參數值進行編碼; 這樣可以避免一些參數傳遞時的問題;

我們爲啥要對參數進行編碼呢?

  1. GET 請求方式中, 我們的參數都編碼到URL上了;
  2. URL 中是不能出現中文和一些特殊符號的
  3. 我們需要傳入一些中文和特殊符號

我們可以想象一下, 如果沒有對參數編碼會有啥後果;

  • 中文是肯定不能傳入了
  • 如果value是 123&a=b 這種, 它會不會變成 123 和 a=b ?

爲了避免這些編碼上不必要的麻煩; 我們因此會對參數名, 參數值進行編碼;
當然, 也有對應的解碼的方法 decodeURIComponent

let plainText = 'h&123=12'
let encodedText = encodeURIComponent(plainText)
let decodedText = decodeURIComponent(encodedText)

POST

當我們發送一個 POST 請求時, 我們需要設置一個請求header的 Content-Type
用於說明, 我們請求的數據是哪種格式的.

POST常見的提交數據的格式有

  • application/x-www-form-urlencoded 效果和GET請求編碼一樣; 但是數據是放在請求體中的;
  • multipart/form-data 用於傳輸大數據; 有 boundary 進行數據分割;
  • application/json RESTful API 接口最常用的數據格式;
  • text/xml xml

POST請求處理請求數據, 它是把數據放在 請求體 中的;
但是, 這不代表POST請求中URL就不能編碼了;
其實, 還是可以加入額外的參數的; 但是一般情況下, 還是遵守約定俗成的規範;

接受請求

之前, 提到了我們可以根據 readyState 來判斷這個請求的狀態;
那麼有哪些狀態呢?

  • UNSENT 0 (uninitialized) or (request not initialized)
  • OPENED 1 (loading) or (server connection established)
  • HEADERS_RECEIVED 2 (loaded) or (request received)
  • LOADING 3 (interactive) or (processing request)
  • DONE 4 (complete) or (request finished and response is ready)
let httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
        // Everything is good, the response was received.
    } else {
        // Not ready yet.
    }

    if (httpRequest.status === 200) {
        var response = JSON.parse(httpRequest.responseText);
        // Perfect!
    } else {
        // There was a problem with the request.
        // For example, the response may have a 404 (Not Found)
        // or 500 (Internal Server Error) response code.
    }
};

接下來我們運行一下代碼試試;

如果我們直接在文件夾中打開html文件;
那麼, 我們會在地址欄中出現類似下面的情況

file:///C:/Users/ju/Desktop/ajax/test.html

這種情況下, 我們發送不了數據的;

打開devTool 我們會看到下面的報錯信息

Access to XMLHttpRequest at ‘http://www.example.org/some.file’ from origin ‘null’ has been blocked by
CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

這就是著名的瀏覽器跨域請求問題;
解決 跨域 有很多方法;

跨域

跨域 是因爲瀏覽器的同源策略導致的;

  1. 爲啥要有跨域呢?

  2. 爲啥瀏覽器要這麼做呢?

  3. 跨域提供了最基礎的安全;

  4. 防止別人惡意的請求;

默認情況下,JavaScript在發送 ajax 請求時,URL的域名端口號必須和當前頁面完全一致。
否則, 就會報跨域的異常;

那麼常見的解決方案有啥啊?

  1. JSONP 但是現在用的少了; 利用了瀏覽器中 一些標籤沒有跨域限制;
  2. NGINX 代理; 做一個靜態資源文件的代理;
  3. 後端修改請求頭, 設置 Access-Control-Allow-Origin
  4. 開發時 利用 webpack-dev-server (內部是用的http-proxy-middleware)

websocket

websocket 最近纔剛剛接觸;
只是做了一個小小的demo;

這裏就不敢胡謅了; 簡單談一些;

爲什麼要有 websocket

首先要知道, 瀏覽器是主動發送請求給服務器的;
但是, 反過來, 服務器是不能主動給瀏覽器發送請求的;

那麼, 在沒有 websocket 之前, 我們處理 瀏覽器和服務器之間的通信;
只能採用 輪詢 的方式;

所謂 輪詢 就是每個一段時間訪問一下服務器;
看看有沒有啥新的消息;

例如在線聊天: 每個client 都發送給server消息;
但是呢, server接受到消息之後, 並不能直接把消息返給其它client;

而只能等client 輪詢的時間到了, 再去把消息返回;

那麼, 面對用戶比較多, 而且對數據的實時性要求比較高;
例如, 視頻會議等; 那麼輪詢就顯得不行了;

websocket 有啥好處

websocket的出現, 就是解決了這一部分問題;
websocket 是利用 TCP 連接, 建立之後, 可以保持長時間的聯繫;
減少了多次請求;

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) {
    console.log("Connection open ...");
    ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
    console.log("Received Message: " + evt.data);
    ws.close();
};

ws.onclose = function(evt) {
    console.log("Connection closed.");
};

XMLHttpRequest 代碼類似;
不過 onmessage 中可以多次觸發;

因爲, 近期學習了Rails, 使用了 Action Cable , 提供的npm包進行的
客戶端與服務端的交互; 這裏就不展開說了;

推薦大家看一下 阮一峯websocket
有簡單明瞭的介紹;

之後, 隨着工作的接觸, 希望能對websocket有更多的瞭解;

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章