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

我的全部文章整理在此篇,將陸續更新收錄:知無涯,行者之路莫言終(個人編程之路)html

零、前言

無論什麼語言,什麼系統,都離不開網絡,這個系列就來深刻一下網絡
首先是挾天子以令諸侯的Http,它把握了整個網絡的命脈。全部人必須對它言聽計從。git

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

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


1、域名解析

chrome中輸入網址敲回車以後,瀏覽器會根據域名找到對應的服務器地址
這裏以個人網站:http://www.toly1994.com/爲例web

1_敲網址.png

尋址簡述.png


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

在此以前先簡單說一下urlspring

基本URL包含
模式(或稱協議)、服務器名稱(或IP地址)、路徑和文件名,
協議://用戶名:密碼@子域名.域名.頂級域名:端口號/目錄/文件名.文件後綴?參數=值#標誌。
複製代碼

url簡述


1.解析域名--Chrome搜索自身DNS緩存

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

DNS緩存.png


2.解析域名--查看本機上的DNS緩存

若是1沒有的話,查看本機上的DNS緩存,ipconfig /displaydns編程

本機DNS查看.png


3.解析域名--查看host文件是否有對應的網址ip

若是2沒有的話,查看host文件是否有對應的網址ip,C:\Windows\System32\drivers\etcjson

查看host文件.png


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

前三步沒有查到,這說明本地無該網站的DNS緩存,由寬帶運營商的服務器進行查詢
若是無緩存,會一級一級的去找,知道找到toly1994.com對應的服務器(即個人服務器),
最後將查到的服務器ip地址返回給剛纔敲網址的瀏覽器後端

解析.png


2、客戶端與服務端創建TCP/IP鏈接:

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

客戶端和服務端.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


3、發送請求與接收響應及四次揮手

上面說到服務端和客戶端創建了鏈接,接下來就是請求響應
在此以前先看一下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. 幾個層級(暫時不深刻)


4、深刻請求與響應

這裏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--

複製代碼

5、如何使用請求頭

上面說了一大堆請求和響應的格式,如今說一下他們的用處
這麼想吧:瀏覽器將請求頭髮給服務器,手機能夠做爲客戶端,職能上和瀏覽器並沒有區別
服務器並不會區分是瀏覽器仍是手機,它只認請求頭,而後作出反應

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的,這裏上傳一張圖片到服務器

/**
 * 經過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;
    }
    ...
  }
}

複製代碼

6、讓網站支持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
相關文章
相關標籤/搜索