概述
微服務(wù)所使用的協(xié)議自然要根據(jù)服務(wù)的特點(diǎn)和類型來(lái)選擇
微服務(wù)類型 | 推薦協(xié)議 | 推薦理由 |
---|---|---|
Web Service | Restful via HTTP | 簡(jiǎn)單實(shí)用, 應(yīng)用廣泛 |
VoIP 及 Telephony Service | 信令用SIP, 媒體用RTP | 支持的終端和媒體網(wǎng)關(guān)眾多 |
多媒體流服務(wù) Multimedia Stream Service | RTP/SRTP/RTSP | 基于傳輸延遲考慮 |
實(shí)時(shí)消息服務(wù) Realtime Message Service | XMPP, PDU via TCP | XMPP 是開源的標(biāo)準(zhǔn)協(xié)議, 效率不高,手機(jī)應(yīng)用不推薦 |
異步消息服務(wù) Async Message Service | JMS/AMQP | ActiveMQ 用 JMS, RabbitMQ 用后者 |
這里說(shuō)的協(xié)議主要是指應(yīng)用層協(xié)議, 傳輸層協(xié)議一般都是TCP, 除非是媒體傳輸考慮用低延遲的 UDP
簡(jiǎn)單來(lái)說(shuō), 一般的信令控制協(xié)議用基于 HTTP 的 REST 協(xié)議就夠了, 或者是基于 TCP /WebSocket 的用 Protobuf 來(lái)封裝應(yīng)用層消息體也不錯(cuò).
SIP/SDP/MGCP 在電話及語(yǔ)音服務(wù)領(lǐng)域應(yīng)用較廣
媒體傳輸一般用 RTP 及 SRTP 或 RTSP 來(lái)承載音頻或視頻, 在多方會(huì)議共享及遠(yuǎn)程控制應(yīng)用中也常用如下協(xié)議
- BFCP -- Binary Floor Control Protocol 二進(jìn)制層控制協(xié)議, 用來(lái)管理共享的資源
- RDP -- Remote Desktop Protocol, 遠(yuǎn)程桌面協(xié)議, 微軟提出并使用在它的遠(yuǎn)程桌面中
- RFB -- Remote Frame Buffer, 遠(yuǎn)程幀緩沖協(xié)議, VNC(Virtual Network Computing )中使用的
REST
先從應(yīng)用最廣的 REST 說(shuō)起, REST (Representational State Transfer) 可表現(xiàn)的狀態(tài)遷移, 是2000年由 Roy Fielding 在他的關(guān)于REST的博士論文中提出的.
REST準(zhǔn)確來(lái)說(shuō)不算是一種協(xié)議, 而是一種設(shè)計(jì)分布式系統(tǒng)的架構(gòu)風(fēng)格, 它是指資源在網(wǎng)絡(luò)中以某種表現(xiàn)形式進(jìn)行狀態(tài)轉(zhuǎn)移.
也就是說(shuō)它是面向資源的, 每種資源都有相對(duì)應(yīng)的URI, 每個(gè)URI 都指向一個(gè)資源, 而資源是可展現(xiàn)的(Representational ) 和有狀態(tài)的(state), 而HTTP 請(qǐng)求則是無(wú)狀態(tài)的, 即它不需要依賴其它的請(qǐng)求, 每個(gè)請(qǐng)求都是相對(duì)獨(dú)立的, 超媒體 Hypermedia 可以通過(guò) 鏈接Link 和 URI 把資源連接起來(lái), Web成功的秘訣也就是用鏈接把世界連接起來(lái).
這里主要指用 HTTP 和 Json 承載的面向資源的 Restful 風(fēng)格的協(xié)議.
由于HTTP協(xié)議比較簡(jiǎn)單, 系統(tǒng)對(duì)外的接口被分為多個(gè)資源 API, 都可以獨(dú)立地進(jìn)行測(cè)試, 并且符合無(wú)狀態(tài)通信的原則, 天然具有比較好的松耦合性和可伸縮性.
在介紹完它的特性之后, 我們就會(huì)明白它為什么會(huì)在分布式系統(tǒng)中大受歡迎
REST 的特點(diǎn)
- REpresentational State Transfer 可表現(xiàn)的狀態(tài)遷移
- Nouns, not verbs, in endpoints 在各端點(diǎn)中資源是名詞而非動(dòng)詞
- All state the client needs is queryable 客戶端所需的所有狀態(tài)是可查詢到的
- Server has a complete picture of system state 服務(wù)端具有完整的系統(tǒng)狀態(tài)
- Particularly useful for intermittently-connected clients 對(duì)間斷性連接的客戶端特別有用
REST 的好處
簡(jiǎn)單
HTTP + Json 地球人都知道摆屯,HTTP method 表示對(duì)于資源的 CRUD 簡(jiǎn)單明了可伸縮
短連接柳弄,無(wú)狀態(tài),易于橫向擴(kuò)展松耦合
基于 URL 和 API 的協(xié)作件已,保持接口簡(jiǎn)單,一致和穩(wěn)定,避免產(chǎn)生復(fù)雜的網(wǎng)狀結(jié)構(gòu)和閉環(huán)破婆,耦合自然沒那么緊
REST 的風(fēng)格
客戶-服務(wù)器(Client-Server)
通信只能由客戶端單方面發(fā)起,表現(xiàn)為請(qǐng)求-響應(yīng)的形式胸囱。無(wú)狀態(tài)(Stateless)
通信的會(huì)話狀態(tài)(Session State)應(yīng)該全部由客戶端負(fù)責(zé)維護(hù)祷舀。緩存(Cache)
響應(yīng)內(nèi)容可以在通信鏈的某處被緩存,以改善網(wǎng)絡(luò)效率。統(tǒng)一接口(Uniform Interface)
通信鏈的組件之間通過(guò)統(tǒng)一的接口相互通信裳扯,以提高交互的可見性抛丽。分層系統(tǒng)(Layered System)
通過(guò)限制組件的行為(即,每個(gè)組件只能“看到”與其交互的緊鄰層)饰豺,將架構(gòu)分解為若干等級(jí)的層亿鲜。按需代碼(Code-On-Demand,可選)
支持通過(guò)下載并執(zhí)行一些代碼(例如Java Applet哟忍、Flash或JavaScript)狡门,對(duì)客戶端的功能進(jìn)行擴(kuò)展。
REST 的特性
- 面向資源 Resource Oriented
要考慮合適的粒度, 可緩存性, 修改頻率和可變性 - 可尋址 Addressability
- 連通性 Connectedness
- 無(wú)狀態(tài) Statelessness
- 統(tǒng)一接口 Uniform Interface
POST, GET, PUT, DELETE , PATCH, HEAD, OPTIONS, TRACE, Connect - 超文本驅(qū)動(dòng) Hypertext Driven
REST 的原則
- 它基于無(wú)狀態(tài), 客戶端-服務(wù)器, 可緩存的通訊協(xié)議
- 資源以易于理解的目錄結(jié)構(gòu)的URI 來(lái)公布
- 以JSON或XML形式傳輸來(lái)表示數(shù)據(jù)對(duì)象和屬性锅很。
- 消息明確地使用了 HTTP 方法(例如其馏,GET,POST爆安,PUT和DELETE)叛复。
- 在HTTP請(qǐng)求與請(qǐng)求之間的無(wú)狀態(tài)交互不在服務(wù)器上存儲(chǔ)客戶端上下文。
狀態(tài)依賴性限制了可擴(kuò)展性, 所以在客戶端存儲(chǔ)會(huì)話狀態(tài)使得橫向擴(kuò)展更加容易
用 HTTP 方法來(lái)表示 CRUD
格式為 [HTTP Method] https://host/{service}/{apiclass}/v{version}
HTTP 方法 | 含義 | 冪等嗎? |
---|---|---|
POST | 創(chuàng)建資源 Create | N |
GET | 獲取或查詢資源 Retrieve | Y |
PUT | 全部替換資源 Update | Y |
DELETE | 刪除資源 Delete | Y |
PATCH | 部分修改資源 | N |
HEAD | 類似于 GET, 但是只傳輸狀態(tài)行和 HTTP 頭 | Y |
OPTIONS | 描述目標(biāo)資源的通信選項(xiàng) | Y |
TRACE | 執(zhí)行沿目標(biāo)資源路徑的消息環(huán)回測(cè)試扔仓。 | Y |
CONNECT | 建立到由給定URI標(biāo)識(shí)的服務(wù)器的隧道 | Y |
所謂冪等性 Idempotence, 它的意思是你調(diào)用一次和調(diào)用多次的效果是一樣的
簡(jiǎn)單列舉一下一些在 REST 中常用的 Http header
常見的 Http 頭域
Header name | Header value example | 備注 |
---|---|---|
Accept | application/json | Respond 406 not acceptable if not support the format |
Content-Type | application/json | 媒體內(nèi)容類型褐奥, |
If-Modified-Since | Respond 304 not modified if the data is not changed | |
If-None-Match | Respond 304 not modified if the data is not changed | |
If-Match | 412 precondition failed if the ETag is not matched | |
ETag | The version of the resource for integrity | |
Location | 201 response contains it contains the URI of the new created resource |
還有一些擴(kuò)展頭:
X-Forwarded-For
HTTP 請(qǐng)求到達(dá) HTTP Server 的時(shí)候往往已經(jīng)過(guò)了反向代理服務(wù)器,所以這時(shí)候看到的 TCP 源地址已經(jīng)不是真正的客戶端應(yīng)用的地址了翘簇,這個(gè)擴(kuò)展頭就是代理服務(wù)器所添加的真正的 source IP 地址, 它由 https://tools.ietf.org/html/rfc7239 定義
比如在 Citrix 的負(fù)載均衡器 netscaler 可以這樣配置, 參見insert client ip to http header
set service Service-HTTP-1 -CIP enabled X-Forwarded-For
Origin 和 Access-Control-Allow-Origin
現(xiàn)代瀏覽器允許突破同源策略(Same Origin Policy), 使用稱為跨域資源共享 CORS(Cross-Origin Resource Sharing), 微軟的 IE8/9 并不支持撬码,需要用 XDomainRequest 替換 XHTTPRequest
例如請(qǐng)求頭如下,表示請(qǐng)求源自哪里:
Origin: https://www.example.com
響應(yīng)頭有
Access-Control-Allow-Headers: AppId, MetricsTicket, ConfID, SiteID, TimeStamp, APPName, Ver
Access-Control-Allow-Methods: OPTIONS, POST, PUT
Access-Control-Allow-Origin: https://www.example.com
這樣一來(lái)版保, XHTTPRequest 對(duì) www.example.com 的訪問就是合法的呜笑。
X-RateLimit-Limit
現(xiàn)在許多 public API 都限定了客戶端的請(qǐng)求頻率, 比如 twitter, github 等彻犁,在響應(yīng)頭中有如下擴(kuò)展頭:
- X-RateLimit-Limit: 單位時(shí)間的訪問上限
- X-RateLimit-Remaining: 剩余的訪問次數(shù)
- X-RateLimit-Reset 訪問次數(shù)重置的時(shí)間
常見的 Http 狀態(tài)碼
2xx
- 200 OK with Etag head
- 201 Created with Location head
- 204 No content
- 206 Partial content
3xx
- 301 Move Permanently
- 302 Found
- 304 Not Modified
4xx
- 401 Unauthorized with WWW-Authorizate head
- 403 Forbidden
- 404 Not Found
- 405 Not Allowed with Allow head
- 406 Not Acceptable
- 409 Conflict
- 410 Gone
- 412 Precondition Failed
- 413 Request Entity Too Large
- 415 Unsupported Media Type
- 451 Unavailable For Legal Reasons
5xx
- 500 Internal Server Error
- 501 Not Implemented
- 502 Bad Gateway
- 503 Service Unavailable with Retry-After head
- 504 Gateway Timeout
URI 設(shè)計(jì)
REST 是面向資源的, 如何定位和尋找資源呢, 就象找人一樣, 資源也需要象人那樣的身份證號(hào)碼 URI
- URI - Uniform Resource Identifier 是指統(tǒng)一資源標(biāo)識(shí)符, 包括 URL 和 URN
- URL - Uniform Resource Locator 是指統(tǒng)一資源定位符, 常見如下的web url , ftp url 等等
- URN - A Uniform Resource Name 是指統(tǒng)一資源名稱, 例如
- tel:+1-816-555-1212
在設(shè)計(jì)資源URI 的時(shí)候,
- 一是要注意它們是名詞,
- 二是要注意區(qū)分單復(fù)數(shù)
- 三是要注意 URI 有長(zhǎng)度限制, 建議小于1k
- 四是要注意在 URI 中不要放未經(jīng)加密的敏感信息, 即使使用TLS/HTTPS
我們可以用
/ 來(lái)表示層次關(guān)系, 例如
[http://api.t.sina.com.cn/groups/groupId/users/$userId-
;, 來(lái)表示并列關(guān)系, 例如
-
用 - 來(lái)提高可讀性, 最好全用小寫, 例如
-
用參數(shù)或者HTTP Range Header 來(lái)限定范圍, 例如
常用方法
緩存控制
我們可以利用一些 HTTP Header 來(lái)控制資源的緩存以及防止并發(fā)問題
- ETag 實(shí)體標(biāo)簽: 一般為資源實(shí)體的哈希值
- Expires 過(guò)期的時(shí)間:
Expires: Thu, 01 Feb 2015 17:00:00 GMT
- Cache-Control 可以有如下屬性
- public 公有緩存
- private 私有緩存
- no-cache 不可緩存
- max-age 緩存的最大時(shí)間, 單位為秒, 一般來(lái)說(shuō) max-age是相對(duì)時(shí)間, 比 Expires 的絕對(duì)時(shí)間要好, 不會(huì)有客戶端和服務(wù)器時(shí)間誤差的問題, 優(yōu)先使用它
- Age 緩存了多少秒
- Last-Modified 資源的最后修改時(shí)間
- If-None-Match 如果不匹配的話
- If-Modified-Since 從何時(shí)起資源有更新
- If-Match 如果匹配的話
- if-Unmodified-Since 從何時(shí)起資源無(wú)更新
當(dāng)服務(wù)器發(fā)現(xiàn)Http請(qǐng)求的 Header 中有 If-None-Match, 就取出它的值, 與緩存中的資源的Etag 比較, 如果一致的話, 返回 304 Not Modified, 節(jié)省從數(shù)據(jù)庫(kù)查詢和網(wǎng)絡(luò)傳輸成本
當(dāng)服務(wù)器發(fā)現(xiàn)Http請(qǐng)求的 Header 中 If-Modified-Since, 就取出它的值, 與緩存中的資源的Last-Modified 比較, 如果 If-Modified-Since中指示的最后修改時(shí)間大于或等于資源的Last-Modified時(shí)間的話, 也返回 304 Not Modified, 即它是從資源最后一次修改之后獲取的, 最近無(wú)更改, 無(wú)需重新查詢
當(dāng)然, 如果不一致的話, 則得重新查詢數(shù)據(jù)庫(kù)并刷新緩存, 返回最新的資源信息, 狀態(tài)為 200 OK
并發(fā)控制
如果多個(gè)請(qǐng)求對(duì)資源進(jìn)行修改, 會(huì)出現(xiàn)丟失修改或者無(wú)效刪除的情況
試想, 張三和李四都是公司的會(huì)計(jì), 張三管考勤, 發(fā)現(xiàn)王二上個(gè)月遲到了三次, 要扣王二三百元錢, 李四管績(jī)效, 要給王二增加一千元獎(jiǎng)金, 假設(shè)王二工資為八千元.
張三修改王二工資為 8000 - 300 = 7700
update payroll set salary=7700 where username="wang2" and salary=8000
李四修改王二工資為 8000 + 1000 = 9000
update payroll set salary=9000 where username="wang2" and salary=8000
強(qiáng)一致性的關(guān)系數(shù)據(jù)庫(kù)使用行級(jí)鎖, 張三和李四只有一個(gè)會(huì)成功, 另一個(gè)會(huì)修改失敗, 返回給其中一個(gè)用戶412錯(cuò)誤, 讓用戶重新修改. 從而使王二的最終薪水為8000-300+1000=8700
一些不支持強(qiáng)事務(wù)的NOSQL存儲(chǔ), 特別是一些KV系統(tǒng)只能根據(jù)key - username來(lái)修改數(shù)據(jù), 就極有可能出現(xiàn)張三和李四都返回成功, 王二工資變成了7700或9000, 而不是正確的8700, 這時(shí)候我們就可以用下面的方法來(lái)減少這種情況的發(fā)生.
- 更新數(shù)據(jù)
當(dāng)服務(wù)器發(fā)現(xiàn)Http Header 中有 If-Match, 就取出它的值, 與當(dāng)前資源的Etag 比較, 如果一致的話, 修改數(shù)據(jù)返回200 OK, 否則返回 412 Precondition Failed
當(dāng)服務(wù)器發(fā)現(xiàn)Http Header 中有if-Unmodified-Since, 就取出它的值, 與當(dāng)前資源的 Last-Modified 進(jìn)行比較, 如果發(fā)現(xiàn)if-Unmodified-Since值大于或等于Last-Modified資源的的話, 修改數(shù)據(jù)返回200 OK, 否則返回 412 Precondition Failed
- 刪除數(shù)據(jù)
當(dāng)服務(wù)器發(fā)現(xiàn)Http Header 中有 if-Match, 就取出它的值, 與當(dāng)前資源的Etag 比較, 如果一致的話,刪除數(shù)據(jù)返回 204 No Content, 否則返回 412 Precondition Failed
當(dāng)服務(wù)器發(fā)現(xiàn)Http Header 中有 if-Unmodified-Since, 就取出它的值, 與當(dāng)前資源的 Last-Modified 進(jìn)行比較, 如果發(fā)現(xiàn)if-Unmodified-Since值大于或等于Last-Modified資源的的話, 修改數(shù)據(jù)返回 204 No Content, 否則返回 412 Precondition Failed
批量處理
例如我們想一次提交多個(gè)請(qǐng)求, 可以用這種方法
Request
POST /api/v1/batch
{
"requests": [
{
"method": "POST",
"path": "/phonenumbers",
“headers”: [ {“Content-Type”: “application/json”}]
"body": {
"number": "86-01012345678",
"type": "mobile"
}
},
{
"method": "POST",
"path": "/telephonydomains/$telephonyDomainID/dialnumbers",
"body": {
"number": "86-01022345678",
"type": "office"
}
}
]
}
Response
HTTP/1.1 200 OK
{
“response” [
{
“status”: 200,
“message”: “OK”
“headers”: [ {“Content-Type”: “application/json”}]
“body”: {}
},
{
“status”: 412,
“message”: “Preconditionl Failed”
“body”: {}
}
]
}
查詢條件超長(zhǎng)或者查詢參數(shù)有敏感信息
用 POST 來(lái)代替 GET , 意謂創(chuàng)建一個(gè)查詢
Request:
POST /accounts/queries
{
“userIds”: [111, 222, 333]
}
Response:
HTTP/1.1200 OK
[
…accounts …
]
異步請(qǐng)求
與同步請(qǐng)求不同的是, 不是立即返回結(jié)果, 而是先給一個(gè) taskId, 可供稍后查詢結(jié)果, 或者在請(qǐng)求時(shí)給一個(gè)回調(diào)URL, 稍后把結(jié)果通知回去
Request
POST https://abc.cde.com/api/v1.0/migrations HTTP 1.1
{
pool: "china",
notifyUri: 'https://abc.cde.com/api/v1/migrations/123'
}
Response
{
"status": 'pending',
"taskID": 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
}
實(shí)例: 帳號(hào)管理的微服務(wù)
光說(shuō)不練假把式, 先拿python 來(lái)寫一個(gè)微服務(wù)原型, 我們平常會(huì)使用諸多網(wǎng)站, 帳號(hào)密碼經(jīng)常忘記, 所以讓我們花一點(diǎn)時(shí)間寫一個(gè)帳號(hào)管理的微服務(wù), 基本功能是記錄我們常用的帳號(hào)和密碼, 以免遺忘, 一切從簡(jiǎn), 不用id, 而是用sitename 作為主鍵
method | description |
---|---|
GET /accounts | 帳戶列表 |
GET /accounts/<siteName> | 帳戶獲取 |
POST /accounts | 帳戶創(chuàng)建 |
PUT /accounts/<siteName> | 帳戶修改 |
DELETE /accounts/<siteName> | 帳戶刪除 |
- 客戶端用 httpie 來(lái)作測(cè)試
- 服務(wù)器端用 python flask 框架來(lái)實(shí)現(xiàn)
- 頁(yè)面的UI 暫且省略
先安裝python 和 virtualenv
brew install python
brew install pyenv-virtualenv
or
sudo pip install virtualenv
再運(yùn)行 virtual env
virtualenv venv
source venv/bin/activate
再安裝所需的類庫(kù)
pip install flask
pip install flask-httpauth
pip install requests
pip install httpie
為簡(jiǎn)單起見, 用 json 文件代替數(shù)據(jù)庫(kù): account.json
{
"jianshu":{
"userName": "walterfan",
"password": "password",
"siteName": "jianshu",
"siteUrl": "http://www.reibang.com/users/e0b365801f48"
},
"weibo":{
"userName": "fanyamin",
"password": "password",
"siteName": "weibo",
"siteUrl": "http://weibo.com/fanyamin"
}
}
源碼如下, 不算空行, 100行之內(nèi)搞定: account.py, 可讀寫json file, 并對(duì)其中的記錄進(jìn)行增刪改查, 暫不考慮性能和其他異常及并發(fā)處理, 差強(qiáng)人意, 僅供演示, 個(gè)人日常使用也行
import os
import json
import requests
from flask_httpauth import HTTPBasicAuth
from flask import make_response
from flask import Flask
from flask import request
from werkzeug.exceptions import NotFound, ServiceUnavailable
app = Flask(__name__)
current_path = os.path.dirname(os.path.realpath(__file__))
auth = HTTPBasicAuth()
users = {
"walter": "pass1"
}
json_file = "{}/account.json".format(current_path)
def read_data():
json_fp = open(json_file, "r")
return json.load(json_fp)
def save_data(accounts):
json_fp = open(json_file, "w")
json.dump(accounts, json_fp, sort_keys = True, indent = 4)
@auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
def generate_response(arg, response_code=200):
response = make_response(json.dumps(arg, sort_keys = True, indent=4))
response.headers['Content-type'] = "application/json"
response.status_code = response_code
return response
@app.route("/", methods=['GET'])
@auth.login_required
def index():
return generate_response({
"username": auth.username(),
"description": "account micro service /accounts"
})
@auth.login_required
@app.route("/accounts", methods=['GET'])
def list_account():
accounts = read_data()
return generate_response(accounts)
#Create account
@auth.login_required
@app.route('/accounts', methods=['POST'])
def create_account():
account = request.json
sitename = account["siteName"]
accounts = read_data()
if sitename in accounts:
return generate_response({"error": "conflict"}, 409)
accounts[sitename] = account
save_data(accounts)
return generate_response(account)
#Retrieve account
@auth.login_required
@app.route('/accounts/<sitename>', methods=['GET'])
def retrieve_account(sitename):
accounts = read_data()
if sitename not in accounts:
return generate_response({"error": "not found"}, 404)
return generate_response(accounts[sitename])
#Update account
@auth.login_required
@app.route('/accounts/<sitename>', methods=['PUT'])
def update_account(sitename):
accounts = read_data()
if sitename not in accounts:
return generate_response({"error": "not found"}, 404)
account = request.json
print(account)
accounts[sitename] = account
save_data(accounts)
return generate_response(account)
#Delete account
@auth.login_required
@app.route('/accounts/<sitename>', methods=['DELETE'])
def delete_account(sitename):
accounts = read_data()
if sitename not in accounts:
return generate_response({"error": "not found"}, 404)
del(accounts[sitename])
save_data(accounts)
return generate_response("", 204)
if __name__ == "__main__":
app.run(port=5000, debug=True)
直接運(yùn)行 python account.py
這個(gè)帳戶管理的RESTful 微服務(wù)就啟動(dòng)了, 用 httpie 測(cè)試一下
- list accounts
(venv) $ http --json --auth walter:pass GET http://localhost:5000/accounts
HTTP/1.0 200 OK
Content-Length: 347
Content-type: application/json
Date: Sat, 10 Dec 2016 15:43:53 GMT
Server: Werkzeug/0.11.11 Python/3.5.1
{
"jianshu": {
"password": "password",
"siteName": "jianshu",
"siteUrl": "http://www.reibang.com/users/e0b365801f48",
"userName": "walterfan"
},
"weibo": {
"password": "password",
"siteName": "weibo",
"siteUrl": "http://weibo.com/fanyamin",
"userName": "fanyamin"
}
}
- create account
http --auth walter:pass --json POST http://localhost:5000/accounts userName=walter password=pass siteName=163 siteUrl=http://163.com
HTTP/1.0 200 OK
Content-Length: 108
Content-type: application/json
Date: Sat, 10 Dec 2016 15:48:59 GMT
Server: Werkzeug/0.11.11 Python/3.5.1
{
"password": "pass",
"siteName": "163",
"siteUrl": "http://163.com",
"userName": "walter"
}
- retrieve account
http --auth walter:pass --json GET http://localhost:5000/accounts/163
HTTP/1.0 200 OK
Content-Length: 108
Content-type: application/json
Date: Sat, 10 Dec 2016 15:49:21 GMT
Server: Werkzeug/0.11.11 Python/3.5.1
{
"password": "pass",
"siteName": "163",
"siteUrl": "http://163.com",
"userName": "walter"
}
- update account
http --auth walter:pass --json PUT http://localhost:5000/accounts/163 userName=walter password=pass123 siteName=163 siteUrl=http://163.com
HTTP/1.0 200 OK
Content-Length: 111
Content-type: application/json
Date: Sat, 10 Dec 2016 15:49:47 GMT
Server: Werkzeug/0.11.11 Python/3.5.1
{
"password": "pass123",
"siteName": "163",
"siteUrl": "http://163.com",
"userName": "walter"
}
- delete account
http --auth walter:pass --json DELETE http://localhost:5000/accounts/163
HTTP/1.0 204 NO CONTENT
Content-Length: 0
Content-type: application/json
Date: Sat, 10 Dec 2016 15:50:18 GMT
Server: Werkzeug/0.11.11 Python/3.5.