本文Demo的完整工程代碼, 客戶(hù)端基于iOS實(shí)現(xiàn), 參考這里的StudyHTTP, 服務(wù)器基于node.js實(shí)現(xiàn), 參考這里的StudyHTTP
目錄
URI, URL和URN
定義
URI(Uniform Resource Identifier, 統(tǒng)一資源標(biāo)識(shí)符)用來(lái)唯一的標(biāo)識(shí)一個(gè)資源
URL(Uniform Resource Locator, 統(tǒng)一資源定位器)可以用來(lái)標(biāo)識(shí)一個(gè)資源, 而且還指明了如何locate這個(gè)資源
URN(Uniform Resource name, 統(tǒng)一資源命名)是通過(guò)名字來(lái)標(biāo)識(shí)資源
關(guān)系和區(qū)別
- URL和URN都是一種URL
URI是以一種抽象的, 高層次概念定義統(tǒng)一資源標(biāo)識(shí), 而URL和URN則是具體的資源標(biāo)識(shí)的方式
URI可以是絕對(duì)的也可以是相對(duì)的, 而URL則必須提供足夠的信息來(lái)定位
更多可以參考你知道URL逾冬、URI和URN三者之間的區(qū)別嗎袄膏?, URI和URL的區(qū)別, URI 和 URL的區(qū)別
RESTful API
什么是RESTful API?
首先要搞清楚什么是REST
REST(Representational State Transfer, 表現(xiàn)層狀態(tài)轉(zhuǎn)化)是一種網(wǎng)絡(luò)架構(gòu)規(guī)范
知道什么是REST, 就知道了什么是RESTful API
實(shí)現(xiàn)了REST規(guī)范的Web API就叫RESTful API
RESTful API有哪些特點(diǎn)?
- 總是使用HTTPS
或許這不屬于REST架構(gòu), 但是卻是最最重要和基礎(chǔ)的一點(diǎn), 詳細(xì)可以參考本文的這一章HTTPS
- 將API的版本號(hào)放入U(xiǎn)RL
https://api.example.com/v1/
- 每個(gè)網(wǎng)址代表一種資源
所以網(wǎng)址中不能有動(dòng)詞, 只能有名詞, 且使用復(fù)數(shù)
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees
- HTTP動(dòng)詞對(duì)應(yīng)資源的具體操作類(lèi)型
GET(SELECT) 從服務(wù)器取出資源(一項(xiàng)或多項(xiàng))
POST(CREATE) 在服務(wù)器新建一個(gè)資源
PUT(UPDATE) 在服務(wù)器更新資源(客戶(hù)端提供改變后的完整資源)
PATCH(UPDATE) 在服務(wù)器更新資源(客戶(hù)端提供改變的屬性)
DELETE(DELETE) 從服務(wù)器刪除資源
例子如下
GET /zoos 列出所有動(dòng)物園
POST /zoos 新建一個(gè)動(dòng)物園
GET /zoos/ID 獲取某個(gè)指定動(dòng)物園的信息
PUT /zoos/ID 更新某個(gè)指定動(dòng)物園的信息(提供該動(dòng)物園的全部信息)
PATCH /zoos/ID 更新某個(gè)指定動(dòng)物園的信息(提供該動(dòng)物園的部分信息)
DELETE /zoos/ID 刪除某個(gè)動(dòng)物園
更多RESTful API, 請(qǐng)參考RESTful API 設(shè)計(jì)指南
HTTP Header
Accept-Encoding
gzip - 使用gzip(GNU zip)壓縮
compress - 使用Unix compress壓縮
deflate - 使用zlib壓縮
identity - 不進(jìn)行編碼
服務(wù)器開(kāi)啟壓縮可以提高傳輸?shù)男? 詳細(xì)可以參考expressjs/compression
Content-Type
常見(jiàn)的Content-Type有
application/json
text/json
application/json和text/json都是指json格式, 前者是官方規(guī)范, 后者是為了兼容, 詳細(xì)參考What is the exact difference between content-type: text/json and application/json?
text/xml
text/html
text/plain
multipart/form-data
multipart/form-data用于POST上傳文件時(shí), 同時(shí)還需要設(shè)置boundary字段
在iOS開(kāi)發(fā)中最有名的網(wǎng)絡(luò)庫(kù)AFNetworking中
- request serializers支持的Content-Type分別是
(1) AFJSONRequestSerializer
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
(2) AFPropertyListRequestSerializer
[mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
(3) AFHTTPRequestSerializer
application/x-www-form-urlencoded
(4) AFStreamingMultipartFormData
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
- response serializers支持的Content-Type分別是
(1) AFJSONResponseSerializer
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
(2) AFXMLParserResponseSerializer
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil];
(3) AFXMLDocumentResponseSerializer
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil];
(4) AFPropertyListResponseSerializer
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil];
(5) AFImageResponseSerializer
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil];
例如
在使用AFHTTPSessionManager的類(lèi)構(gòu)造方法+ (instancetype)manager生成對(duì)象時(shí), 由于response serializer默認(rèn)是AFJSONResponseSerializer
self.responseSerializer = [AFJSONResponseSerializer serializer];
如果response的Content-Type不是json類(lèi)型的話, 那么就會(huì)出現(xiàn)類(lèi)似下面的錯(cuò)誤
2016-09-21 14:22:37.246 StudyHTTP[7589:623717] error = Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: text/html" UserInfo={com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x610000225ba0> { URL: http://localhost:3000/users/ } { status code: 200, headers {
Connection = "keep-alive";
"Content-Length" = 23;
"Content-Type" = "text/html; charset=utf-8";
Date = "Wed, 21 Sep 2016 06:22:35 GMT";
Etag = "W/\"17-i6pE/Ux9hQaoN6ksprpWig\"";
"Proxy-Connection" = "Keep-alive";
"X-Powered-By" = Express;
} }, NSErrorFailingURLKey=http://localhost:3000/users/, com.alamofire.serialization.response.error.data=<72657370 6f6e6420 77697468 20612072 65736f75 726365>, NSLocalizedDescription=Request failed: unacceptable content-type: text/html}
Range
對(duì)于只需獲取部分資源的范圍請(qǐng)求, 包含首部字段Range即可告知服務(wù)器資源的指定范圍
使用這個(gè)字段就可以實(shí)現(xiàn)文件的斷點(diǎn)續(xù)傳
User-Agent
將創(chuàng)建請(qǐng)求的瀏覽器和用戶(hù)代理名稱(chēng)等信息傳達(dá)給服務(wù)器
例如
當(dāng)使用Safari瀏覽打開(kāi)百度時(shí)
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50
關(guān)于更多Content-Type, 可以參考常用對(duì)照表 HTTP Content-type
HTTP Status Code
2XX 成功狀態(tài)碼 請(qǐng)求正常處理完畢
200 OK
201 Created
202 Accepted
204 No Content
206 Partial Content
3XX 重定向狀態(tài)碼 需要進(jìn)行附加操作以完成請(qǐng)求
301 Moved Permanently(永久重定向)
302 Found(臨時(shí)重定向)
4XX 客戶(hù)端錯(cuò)誤狀態(tài)碼 服務(wù)器無(wú)法處理請(qǐng)求
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
5XX 服務(wù)器錯(cuò)誤狀態(tài)碼 服務(wù)器處理請(qǐng)求出錯(cuò)
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
HTTP數(shù)據(jù)傳遞
GET方式的參數(shù)會(huì)添加到URL中
例如URL請(qǐng)求和參數(shù)設(shè)置如下(實(shí)現(xiàn)基于iOSAFNetworking)
[self.sessionManager GET:@"http://localhost:3000/users/" parameters:@{@"key": @"value"} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"responseObject = %@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"error = %@", error);
}];
那么HTTP請(qǐng)求實(shí)際的URL是
http://localhost:3000/users/?key=value
這時(shí), 服務(wù)器通過(guò)request的query取出數(shù)據(jù)
console.log(req.query); // { key: 'value' }
POST方式的參數(shù)會(huì)添加到body中
例如URL請(qǐng)求和參數(shù)設(shè)置如下(實(shí)現(xiàn)基于iOSAFNetworking)
[self.sessionManager POST:@"http://localhost:3000/users/" parameters:@{@"key": @"value"} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"responseObject = %@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"error = %@", error);
}];
此時(shí)HTTP請(qǐng)求的body就是
key=value
這時(shí), 服務(wù)器通過(guò)request的body取出數(shù)據(jù)
console.log(req.body); // { key: 'value' }
HTTPS
發(fā)展至今, HTTPS已經(jīng)可以算是標(biāo)配了, 當(dāng)然中國(guó)的網(wǎng)絡(luò)環(huán)境總是"慢人一步", 為什么要如此重視HTTPS呢?
這是因?yàn)镠TTP存在以下問(wèn)題
通信使用明文(不加密), 內(nèi)容可能會(huì)被竊聽(tīng)
不驗(yàn)證通信方的身份, 因此有可能遭遇偽裝
無(wú)法證明報(bào)文的完整性, 所以有可能已遭篡改
而HTTP加上加密處理和認(rèn)證以及完整性保護(hù)后即是HTTPS
簡(jiǎn)單來(lái)說(shuō)HTTPS(HTTP secure) = HTTP + 加密 + 認(rèn)證 + 完整性保護(hù)
至于HTTPS為什么安全的具體分析, 可以參考SSL/TLS協(xié)議運(yùn)行機(jī)制的概述
這里總結(jié)下HTTPS與HTTP的幾個(gè)明顯差異
HTTPS = HTTP over SSL/TLS (其中: SSL是Secure Sockets Layer的縮寫(xiě), TLS是Transport Layer Security的縮寫(xiě))
HTTPS需要到CA(Certificate Authority)申請(qǐng)證書(shū)
HTTP默認(rèn)采用80端口, 而HTTPS默認(rèn)采用443端口
HTTPS的簡(jiǎn)單流程是這樣子的
客戶(hù)端向服務(wù)器端索要并驗(yàn)證公鑰
雙方協(xié)商生成"對(duì)話密鑰"
雙方采用"對(duì)話密鑰"進(jìn)行加密通信
詳細(xì)的過(guò)程可以參考圖解SSL/TLS協(xié)議
參考
更多文章, 請(qǐng)支持我的個(gè)人博客