網路篇:協天子令諸侯[-Http-]

張風捷特烈發表於2019-03-12

個人所有文章整理在此篇,將陸續更新收錄:知無涯,行者之路莫言終(我的程式設計之路)

零、前言

不管什麼語言,什麼系統,都離不開網路,這個系列就來深入一下網路
首先是挾天子以令諸侯的Http,它把握了整個網路的命脈。所有人必須對它言聽計從。

本文主要聚焦
1.dns的定址(域名解析)  
2.tcp/ip的三次握手,建立連線  
3.客戶端請求和服務端響應的詳細分析  
4.騰訊雲免費ssl證書獲取,以及基於springboot2設定https支援
複製程式碼

注:本文的服務端程式碼已放在Github,如果沒有基礎,可以參見我的SpringBoot入門級系列
如果不想接觸後端,本文也可以看,不過理解方面多少會有些欠缺,
畢竟http是客戶端服務端兩個人的事,撇看一者來看,是不現實的。


一、域名解析

chrome中輸入網址敲回車之後,瀏覽器會根據域名找到對應的伺服器地址
這裡以我的網站:http://www.toly1994.com/為例

1_敲網址.png

定址簡述.png


0.url簡述:統一資源定位符(Uniform Resource Locator)

在此之前先簡單說一下url

基本URL包含
模式(或稱協議)、伺服器名稱(或IP地址)、路徑和檔名,
協議://使用者名稱:密碼@子域名.域名.頂級域名:埠號/目錄/檔名.檔案字尾?引數=值#標誌。
複製程式碼

url簡述


1.解析域名--Chrome搜尋自身DNS快取

也就是根據域名找到對應的ip地址:首先看瀏覽器自身有沒有快取:
chrome://net-internals/#dns

DNS快取.png


2.解析域名--檢視本機上的DNS快取

如果1沒有的話,檢視本機上的DNS快取,ipconfig /displaydns

本機DNS檢視.png


3.解析域名--檢視host檔案是否有對應的網址ip

如果2沒有的話,檢視host檔案是否有對應的網址ip,C:\Windows\System32\drivers\etc

檢視host檔案.png


4.解析域名--外部查詢

前三步沒有查到,這說明本地無該網站的DNS快取,由寬頻運營商的伺服器進行查詢
如果無快取,會一級一級的去找,知道找到toly1994.com對應的伺服器(即我的伺服器),
最後將查到的伺服器ip地址返回給剛才敲網址的瀏覽器

解析.png


二、客戶端與服務端建立TCP/IP連線:

為了簡單些,使用http://www.toly1994.com:8080/swords/21
客戶端在訪問時,第一步就是查詢域名所對的ip地址(即伺服器住哪)

客戶端和服務端.png

查詢ip.png


0.TCP報文圖

網上的圖有點醜,這裡特意畫了一幅,對於TCP/IP會有專文總結,
這裡先認識兩個控制位:SYN和ACK

SYN:同步序列編號(Synchronize Sequence Numbers),在建立連線時使用
|-- 當SYN=1,ACK=0時,表示這是一個請求建立連線的報文段;
|-- 當SYN=1,ACK=1時,表示對方同意建立連線。
|-- SYN=1,說明這是一個請求建立連線或同意建立連線的報文。只有在前兩次握手中SYN才置為1。

ACK:確認序號標誌(Acknowledgment):前面的確認號欄位是否有效。
|-- 只有當ACK=1時,前面的確認號欄位才有效。為0表示報文中不含確認資訊,忽略確認號欄位。
|-- TCP規定,連線建立後,ACK必須為1。
複製程式碼

TCP報文格式.png


1.第一次握手:問一下伺服器在不在

客戶端傳送SYN=1,seq=J(J為隨機數字)的報文給伺服器
服務端看到SYN=1,知道客戶端要請求連線

第一次握手.png


2.第二次握手:伺服器說在

服務端傳送SYN=1,ACK=1,seq=K(K為隨機數字),ack=J+1的報文給客戶端
客戶端看到SYN=1,ack=J+1,便知道服務端給自己回話了

第二次握手.png


3.第三次握手:客戶端說我也還在

客戶端傳送ACK=1,ack=K+1的報文給伺服器
服務端看到ack=K+1,知道客戶端收到了剛才的話

第三次握手.png

這樣就建立了一個穩固的TCP/IP連線

建立連線.png


三、傳送請求與接收響應及四次揮手

上面說到服務端和客戶端建立了連線,接下來就是請求響應
在此之前先看一下chrome試中和網路相關的工具

chrome.png


1.請求

請球.png

請求頭.png


2.服務端接收到請求

請求是由客戶端發出的,也就是chrome瀏覽器程式,關於Upgrade-Insecure-Requests詳見
客戶端將自己的情況和請求的東西用請求頭髮送給伺服器,伺服器根據請求頭找到資源

伺服器根據客戶端請求找到資源.png

|-- 請求方式 資源路徑 http版本
GET /swords/21 HTTP/1.1
|-- 請求的伺服器域名+埠號
Host: 192.168.10.104:8080
|-- 連線引數
Connection: keep-alive
|-- 快取控制引數
Cache-Control: max-age=0
|-- 升級-不安全-的請求:
Upgrade-Insecure-Requests: 1
|-- 使用者代理:告訴伺服器自己的現狀
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
|-- 該次請求可以接收的檔案型別
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|-- 宣告客戶端支援的編碼型別
Accept-Encoding: gzip, deflate
|-- 宣告瀏覽器支援的語言
Accept-Language: zh-CN,zh;q=0.9
複製程式碼

3.接收響應

chrome的除錯工具展現的已經處理過了,並非原樣,這裡先看一下,等會再看原生的,
伺服器傳送響應給客戶端,客戶端根據響應進行下一步動作

響應.png

伺服器通過響應傳遞資料.png


4.四次揮手

第一次揮手.png

第二次揮手.png

第三次揮手.png

第四次揮手.png


5. 幾個層級(暫時不深入)

網路篇:協天子令諸侯[-Http-]


四、深入請求與響應

這裡chrome除錯不夠用了,使用PostMan進行請求,使用Fiddler進行抓包,
基本使用很簡單,裝上就行了。以下的測試認真看,這是以後經常用到的
網上很多要不就是講的太抽象,要麼就是太片面,這裡好好把一些凌亂的點理一下

抓包.png

預設是所有網路請求都會顯示在左側,你可以這樣過濾:

過濾操作.png


1.GET:最簡單的請求:

http://192.168.10.104:8080/swords/21

請求:
GET http://192.168.10.104:8080/swords/21 HTTP/1.1
cache-control: no-cache
Postman-Token: e72160d9-48db-4933-8c10-e0157c0f86db
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: 192.168.10.104:8080
accept-encoding: gzip, deflate
Connection: keep-alive
複製程式碼
響應:

注意響應頭和響應體(資料)之間有一個空行
Content-Length這個欄位很重要,它表示響應體(資料)的位元組大小(如下圖:)

Content-Length.png

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Fri, 01 Feb 2019 04:56:35 GMT
Content-Length: 406

{"id":21,"name":"Excalibur","info":"Excalibur是傳說中不列顛國王亞瑟王從湖之仙女那得到的聖劍。此劍在是精靈在阿瓦隆(Avalon)所打造,劍鍔由黃金所鑄、劍柄上鑲有寶石,並因其鋒刃削鐵如泥","imgurl":"http://localhost:8080/imgs/timg.jpg","create_time":"2018-07-17T08:29:36.000+0000","modify_time":"2018-07-17T08:29:36.000+0000","origin":"亞瑟王"}
複製程式碼

2.GET:請求中加入請求引數(params):

上面是將引數作為url的最後一級,是一種Restful的書寫規範
這裡,將請求的引數加在url後,是url自身書寫規範,和上面基本沒什麼區別:
http://192.168.10.104:8080/swords/id?id=21

請求:
GET http://192.168.10.104:8080/swords/id?id=21 HTTP/1.1
cache-control: no-cache
Postman-Token: b7d97a6f-4c97-4651-8ccf-16541e24747c
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: 192.168.10.104:8080
accept-encoding: gzip, deflate
Connection: keep-alive
複製程式碼
響應:
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Fri, 01 Feb 2019 05:35:24 GMT
Content-Length: 406

{"id":21,"name":"Excalibur","info":"Excalibur是傳說中不列顛國王亞瑟王從湖之仙女那得到的聖劍。此劍在是精靈在阿瓦隆(Avalon)所打造,劍鍔由黃金所鑄、劍柄上鑲有寶石,並因其鋒刃削鐵如泥","imgurl":"http://localhost:8080/imgs/timg.jpg","create_time":"2018-07-17T08:29:36.000+0000","modify_time":"2018-07-17T08:29:36.000+0000","origin":"亞瑟王"}
複製程式碼

3.POST:請求中加入請求引數(params)

與GET:請求中加入請求引數(params)唯一的區別就是請求方法不同
使用POST+請求引數,引數依然在url中,但不明文顯示,注意與下面POST提交表單的區別
POST表單時請求含有請求體,而POST+請求引數並沒有請求體,引數依然通過url傳遞

引數形式post.png

POST http://192.168.10.104:8080/api/sword?name=擎天劍&imgurl=http://192.168.10.104:8080/imgs/oQttHzCOUqeOatEH.jpg&info=天地一劍,開世擎天&origin=天晴仞 HTTP/1.1
cache-control: no-cache
Postman-Token: c60f5455-8263-4928-a2b3-278af9d198fd
User-Agent: PostmanRuntime/7.6.0
Accept: */*
Host: 192.168.10.104:8080
cookie: JSESSIONID=8B28A94404EF579B0E246ECD7FD04056
accept-encoding: gzip, deflate
content-length: 0
Connection: keep-alive

複製程式碼
響應:
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 03 Feb 2019 15:00:35 GMT
Content-Length: 227

{"code":200,"msg":"操作成功","data":{"id":70,"name":"擎天劍","info":"天地一劍,開世擎天","imgurl":"http://192.168.10.104:8080/imgs/oQttHzCOUqeOatEH.jpg","create_time":null,"modify_time":null,"origin":"天晴仞"}}
複製程式碼

4.POST:表單提交

我們都填過表單,如登陸介面,表單採用post方式提交
這時候請求體(Body)就有用了,可以將一些額外的資料傳遞給伺服器
這樣的好處就是不用將資料暴露在url裡了,注意一下表格資料傳送的格式:

post表單提交.png

請求:
POST http://192.168.10.104:8080/api/sword HTTP/1.1
cache-control: no-cache
Postman-Token: cf6cb7e3-e66d-4339-9685-54f46af7db12
User-Agent: PostmanRuntime/7.6.0
Accept: */*
Host: 192.168.10.104:8080
cookie: JSESSIONID=8B28A94404EF579B0E246ECD7FD04056
accept-encoding: gzip, deflate
content-type: multipart/form-data; boundary=--------------------------789466732494020503103134
content-length: 567
Connection: keep-alive

----------------------------789466732494020503103134
Content-Disposition: form-data; name="name"

擎天劍
----------------------------789466732494020503103134
Content-Disposition: form-data; name="imgurl"

http://192.168.10.104:8080/imgs/oQttHzCOUqeOatEH.jpg
----------------------------789466732494020503103134
Content-Disposition: form-data; name="info"

天地一劍,開世擎天
----------------------------789466732494020503103134
Content-Disposition: form-data; name="origin"

天晴仞
----------------------------789466732494020503103134--

複製程式碼
響應:
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Mon, 04 Feb 2019 05:00:35 GMT
Content-Length: 227

{"code":200,"msg":"操作成功","data":{"id":70,"name":"擎天劍","info":"天地一劍,開世擎天","imgurl":"http://192.168.10.104:8080/imgs/oQttHzCOUqeOatEH.jpg","create_time":null,"modify_time":null,"origin":"天晴仞"}}
複製程式碼

5.POST-表單上傳檔案

注意一下這裡檔案上傳時請求的格式,可以和上面的表單對比一下

表單上傳檔案.png

上傳成功.png

請求:
POST http://192.168.10.104:8080/upload HTTP/1.1
cache-control: no-cache
Postman-Token: c12cd0dc-fbcd-4726-9c97-ebacc6498d26
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: 192.168.10.104:8080
accept-encoding: gzip, deflate
content-type: multipart/form-data; boundary=--------------------------131785098353427999614106
content-length: 345
Connection: keep-alive

----------------------------131785098353427999614106
Content-Disposition: form-data; name="file"; filename="應龍.txt"
Content-Type: text/plain

《應龍》--張風捷特烈
 一遊小池兩歲月,
 洗卻凡世幾閒塵。
 時逢雷霆風會雨,
 應乘扶搖化入雲。
----------------------------131785098353427999614106--

複製程式碼
響應:
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 24
Date: Fri, 01 Feb 2019 06:53:03 GMT

應龍.txt上傳成功!

複製程式碼

6.POST-傳遞原生資料

也就是在客戶端請求是攜帶請求的額外原生資料(如下),服務端可以拿到這些資料

原生資料格式.png

post傳遞String資料.png

服務端.png

請求:

可見請求體的資料也是和請求頭隔著一行

POST http://192.168.10.104:8080/postString HTTP/1.1
cache-control: no-cache
Postman-Token: e532e186-4a4c-4a9f-82bb-8045b1cd9403
Content-Type: text/plain
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: 192.168.10.104:8080
accept-encoding: gzip, deflate
content-length: 53
Connection: keep-alive

海的彼岸有我未曾見證的風采--創世神無
複製程式碼
響應:

伺服器裡我讓資料原樣返回,當然你也可以處理一下再返回

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 53
Date: Fri, 01 Feb 2019 06:19:52 GMT

海的彼岸有我未曾見證的風采--創世神無
複製程式碼

7.POST-二進位制檔案

注意一下,傳遞二進位制檔案和表單傳遞檔案、原生資料的區別
|--POST-二進位制檔案 格式上同傳遞 原生資料,由於是二進位制流,可以傳遞任意的資料
|--POST-二進位制檔案和表單上傳檔案都能上傳檔案,但請求體是完全不同的

請求:
POST http://192.168.10.104:8080/PostFile HTTP/1.1
cache-control: no-cache
Postman-Token: 607a26e6-9c84-4b6b-97e9-3f539cf9a150
Content-Type: text/plain
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: 192.168.10.104:8080
accept-encoding: gzip, deflate
content-length: 137
Connection: keep-alive

《應龍》--張風捷特烈
 一遊小池兩歲月,
 洗卻凡世幾閒塵。
 時逢雷霆風會雨,
 應乘扶搖化入雲。
複製程式碼
響應:
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 7
Date: Fri, 01 Feb 2019 07:28:35 GMT

SUCCESS
複製程式碼

關於PUT,DELETE等於請求與POST基本一致,就不多說了


8.再認識表單

下面是一個簡單的表單,介面未優化,來看一下多個欄位是如何請求的
這樣也許你會對錶單有更深的認識,也會對多檔案上傳有思路

表單.png

POST http://192.168.10.104:8080/submit_form HTTP/1.1
Host: 192.168.10.104:8080
Connection: keep-alive
Content-Length: 626
Cache-Control: max-age=0
Origin: http://192.168.10.104:8080
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7Mqt2T4cA2gNVkCa
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://192.168.10.104:8080/add_form
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

------WebKitFormBoundary7Mqt2T4cA2gNVkCa
Content-Disposition: form-data; name="name"

弒神劍
------WebKitFormBoundary7Mqt2T4cA2gNVkCa
Content-Disposition: form-data; name="info"

一劍弒神
------WebKitFormBoundary7Mqt2T4cA2gNVkCa
Content-Disposition: form-data; name="origin"

噬神者
------WebKitFormBoundary7Mqt2T4cA2gNVkCa
Content-Disposition: form-data; name="file"; filename="應龍.txt"
Content-Type: text/plain

《應龍》--張風捷特烈
 一遊小池兩歲月,
 洗卻凡世幾閒塵。
 時逢雷霆風會雨,
 應乘扶搖化入雲。
------WebKitFormBoundary7Mqt2T4cA2gNVkCa--

複製程式碼

五、如何使用請求頭

上面說了一大堆請求和響應的格式,現在說一下他們的用處
這麼想吧:瀏覽器將請求頭髮給伺服器,手機可以作為客戶端,職能上和瀏覽器並無區別
伺服器並不會區分是瀏覽器還是手機,它只認請求頭,然後做出反應

1.手機POST字串到伺服器

客戶端使用socket連線服務端,通過socket的輸出流將請求頭寫給伺服器
伺服器看到請求頭就會做出相應的反應,這裡的請求頭就是四-6的請求頭

/**
 * 通過ip和埠連線服務端核心程式碼
 *
 * @param ip   ip地址
 * @param port 埠
 */
private void connServer(String ip, int port) {
    String header = "POST http://192.168.10.105:8080/postString HTTP/1.1\n" +
            "cache-control: no-cache\n" +
            "Postman-Token: e532e186-4a4c-4a9f-82bb-8045b1cd9403\n" +
            "Content-Type: text/plain\n" +
            "User-Agent: PostmanRuntime/7.4.0\n" +
            "Accept: */*\n" +
            "Host: 192.168.10.105:8080\n" +
            "accept-encoding: gzip, deflate\n" +
            "content-length: 53\n" +
            "Connection: keep-alive\n" +
            "\n" +
            "海的彼岸有我未曾見證的風采--創世神無";
    try {
        //1.建立客戶端Socket物件(ip,埠)
        mSocket = new Socket(ip, port);
        //2.通過socket物件獲取位元組輸出流,幷包裝成PrintWriter----用於傳送給服務端資料
        mPwOut = new PrintWriter(mSocket.getOutputStream(), true);
        //3.通過socket物件獲取位元組輸出流
        mPwOut.write(header);
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        mPwOut.close();
    }
}

|-- Android中使用
new Thread(()->{
    connServer("192.168.10.105", 8080);
}).start();
複製程式碼

2.服務端的處理

通過HttpServletRequest獲取輸入流,進而得到請求體
你可以根據輸入流來自定義一些操作,如儲存,轉換等

服務端.png

---->[SwordController#postString]----------------------------------
@PostMapping("/postString")
public String postString(HttpServletRequest request) {
    ServletInputStream is = null;
    try {
        is = request.getInputStream();
        StringBuilder sb = new StringBuilder();
        byte[] buf = new byte[1024];
        int len = 0;
        while ((len = is.read(buf)) != -1) {
            sb.append(new String(buf, 0, len));
        }
        System.out.println(sb.toString());
        return sb.toString();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (is != null) {
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}
複製程式碼

3.上傳二進位制檔案

請求頭是四-7的,這裡上傳一張圖片到伺服器

網路篇:協天子令諸侯[-Http-]

/**
 * 通過ip和埠連線服務端核心程式碼
 *
 * @param ip   ip地址
 * @param port 埠
 */
private void connServerPostFile(String ip, int port) {
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.pic_22);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    byte[] datas = baos.toByteArray();
    String header = "POST http://192.168.10.105:8080/PostFile HTTP/1.1\n" +
            "cache-control: no-cache\n" +
            "Postman-Token: 607a26e6-9c84-4b6b-97e9-3f539cf9a150\n" +
            "Content-Type: text/plain\n" +
            "User-Agent: PostmanRuntime/7.4.0\n" +
            "Accept: */*\n" +
            "Host: 192.168.10.105:8080\n" +
            "accept-encoding: gzip, deflate\n" +
            "content-length: " + datas.length + "\n" +
            "Connection: keep-alive\n" +
            "\n";
    try {
        //1.建立客戶端Socket物件(ip,埠)
        mSocket = new Socket(ip, port);
        mOs = mSocket.getOutputStream();
        mOs.write(header.getBytes());
        mOs.write(datas);
        //3.通過socket物件獲取位元組輸出流
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            mOs.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
複製程式碼

也就是說伺服器只認請求,只有頭對了,它就提供服務。
就像員工吃食堂,管你張三李四,有工作牌就能食堂就提供打飯服務。
網路框架HttpURLConnection,okHttp底層都少不了對這些的封裝


4.小看一下okHttp對錶單的拼接

okHttp也是按照Http的請求格式規範來對錶單進行拼合的
如果在用okHttp時,能意識到你的請求是什麼樣子的,會不會視野更開闊呢?

public final class MultipartBody extends RequestBody {
    public static final MediaType MIXED = MediaType.get("multipart/mixed");
    public static final MediaType ALTERNATIVE =   MediaType.get("multipart/alternative");
    public static final MediaType DIGEST = MediaType.get("multipart/digest");
    public static final MediaType PARALLEL = MediaType.get("multipart/parallel");
    public static final MediaType FORM = MediaType.get("multipart/form-data");

--->private static final byte[] COLONSPACE = {':', ' '};
--->private static final byte[] CRLF = {'\r', '\n'};
--->private static final byte[] DASHDASH = {'-', '-'};

--->private final ByteString boundary;//分割線
    private final MediaType originalType;
    private final MediaType contentType;
    private final List<Part> parts;
    private long contentLength = -1L;

  MultipartBody(ByteString boundary, MediaType type, List<Part> parts) {
    this.boundary = boundary;
    this.originalType = type;
    this.contentType = MediaType.get(type + "; boundary=" + boundary.utf8());
    this.parts = Util.immutableList(parts);
  }
  
  ...
  public static final class Part {
    public static Part create(RequestBody body) {
      return create(null, body);
    }
    
    public static Part create(@Nullable Headers headers, RequestBody body) {
      if (body == null) {
        throw new NullPointerException("body == null");
      }
      if (headers != null && headers.get("Content-Type") != null) {
        throw new IllegalArgumentException("Unexpected header: Content-Type");
      }
      if (headers != null && headers.get("Content-Length") != null) {
        throw new IllegalArgumentException("Unexpected header: Content-Length");
      }
      return new Part(headers, body);
    }
    //下面是拼合表單的方法
    public static Part createFormData(String name, String value) {
      return createFormData(name, null, RequestBody.create(null, value));
    }

    public static Part createFormData(String name, @Nullable String filename, RequestBody body) {
      if (name == null) {
        throw new NullPointerException("name == null");
      }
--->  StringBuilder disposition = new StringBuilder("form-data; name=");
      appendQuotedString(disposition, name);

      if (filename != null) {
        disposition.append("; filename=");
        appendQuotedString(disposition, filename);
      }

      Headers headers = new Headers.Builder()
--->      .addUnsafeNonAscii("Content-Disposition", disposition.toString())
          .build();

      return create(headers, body);
    }
    ...
  }

  public static final class Builder {
    private final ByteString boundary;
    private MediaType type = MIXED;
    private final List<Part> parts = new ArrayList<>();

    public Builder() {
      this(UUID.randomUUID().toString());
    }

    public Builder(String boundary) {
      this.boundary = ByteString.encodeUtf8(boundary);
    }

    /**
     * 設定 MIME type
     */
    public Builder setType(MediaType type) {
      if (type == null) {
        throw new NullPointerException("type == null");
      }
      if (!type.type().equals("multipart")) {
--->    throw new IllegalArgumentException("multipart != " + type);
      }
      this.type = type;
      return this;
    }
    ...
  }
}

複製程式碼

六、讓網站支援Https

1.進入控制檯

控制檯.png


2.獲取證書

ssl.png


3.填表

填表.png

自動.png


4.下載證書

下載證書.png


5.SpringBoot專案配置

配置好後打包上線

專案配置.png


6.效果

支援https.png

OK 這就可以通過https訪問了,簡單支援一下,其他的就不深究了
這篇也挺長了,關於快取、伺服器的響應碼就放到下一篇吧。


後記:捷文規範

1.本文成長記錄及勘誤表
專案原始碼 日期 附錄
V0.1--無 2018-3-12
V0.2--無 2018-3-19 增加四次揮手

釋出名:網路篇:協天子令諸侯[-Http-]
捷文連結:juejin.im/post/5c87a7…

2.更多關於我
筆名 QQ 微信
張風捷特烈 1981462002 zdl1994328

我的github:github.com/toly1994328
我的簡書:www.jianshu.com/u/e4e52c116…
我的簡書:www.jianshu.com/u/e4e52c116…
個人網站:www.toly1994.com

3.宣告

1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援

icon_wx_200.png

相關文章