微服務(wù)協(xié)議篇之REST

概述

微服務(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)用較廣

媒體傳輸一般用 RTPSRTPRTSP 來(lái)承載音頻或視頻, 在多方會(huì)議共享及遠(yuǎn)程控制應(yīng)用中也常用如下協(xié)議

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)格

  1. 客戶-服務(wù)器(Client-Server)
    通信只能由客戶端單方面發(fā)起,表現(xiàn)為請(qǐng)求-響應(yīng)的形式胸囱。

  2. 無(wú)狀態(tài)(Stateless)
    通信的會(huì)話狀態(tài)(Session State)應(yīng)該全部由客戶端負(fù)責(zé)維護(hù)祷舀。

  3. 緩存(Cache)
    響應(yīng)內(nèi)容可以在通信鏈的某處被緩存,以改善網(wǎng)絡(luò)效率。

  4. 統(tǒng)一接口(Uniform Interface)
    通信鏈的組件之間通過(guò)統(tǒng)一的接口相互通信裳扯,以提高交互的可見性抛丽。

  5. 分層系統(tǒng)(Layered System)
    通過(guò)限制組件的行為(即,每個(gè)組件只能“看到”與其交互的緊鄰層)饰豺,將架構(gòu)分解為若干等級(jí)的層亿鲜。

  6. 按需代碼(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
URI

在設(shè)計(jì)資源URI 的時(shí)候,

  • 一是要注意它們是名詞,
  • 二是要注意區(qū)分單復(fù)數(shù)
  • 三是要注意 URI 有長(zhǎng)度限制, 建議小于1k
  • 四是要注意在 URI 中不要放未經(jīng)加密的敏感信息, 即使使用TLS/HTTPS

我們可以用

常用方法

緩存控制

我們可以利用一些 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.

參考文檔與鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末叫胁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子汞幢,更是在濱河造成了極大的恐慌驼鹅,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件森篷,死亡現(xiàn)場(chǎng)離奇詭異输钩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)仲智,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門张足,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人坎藐,你說(shuō)我怎么就攤上這事为牍『甙螅” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵碉咆,是天一觀的道長(zhǎng)抖韩。 經(jīng)常有香客問我,道長(zhǎng)疫铜,這世上最難降的妖魔是什么茂浮? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮壳咕,結(jié)果婚禮上席揽,老公的妹妹穿的比我還像新娘。我一直安慰自己谓厘,他們只是感情好幌羞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著竟稳,像睡著了一般属桦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上他爸,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天聂宾,我揣著相機(jī)與錄音,去河邊找鬼诊笤。 笑死系谐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讨跟。 我是一名探鬼主播蔚鸥,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起塞茅,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嘴办,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后乾巧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體句喜,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年沟于,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咳胃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旷太,死狀恐怖展懈,靈堂內(nèi)的尸體忽然破棺而出销睁,到底是詐尸還是另有隱情,我是刑警寧澤存崖,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布冻记,位于F島的核電站,受9級(jí)特大地震影響来惧,放射性物質(zhì)發(fā)生泄漏冗栗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一供搀、第九天 我趴在偏房一處隱蔽的房頂上張望隅居。 院中可真熱鬧,春花似錦葛虐、人聲如沸胎源。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乒融。三九已至,卻和暖如春摄悯,著一層夾襖步出監(jiān)牢的瞬間赞季,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工奢驯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留申钩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓瘪阁,卻偏偏與公主長(zhǎng)得像撒遣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子管跺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一說(shuō)到REST义黎,我想大家的第一反應(yīng)就是“啊,就是那種前后臺(tái)通信方式豁跑×椋”但是在要求詳細(xì)講述它所提出的各個(gè)約束,以及如...
    時(shí)待吾閱讀 3,425評(píng)論 0 19
  • API定義規(guī)范 本規(guī)范設(shè)計(jì)基于如下使用場(chǎng)景: 請(qǐng)求頻率不是非常高:如果產(chǎn)品的使用周期內(nèi)請(qǐng)求頻率非常高艇拍,建議使用雙通...
    有涯逐無(wú)涯閱讀 2,540評(píng)論 0 6
  • 本篇文章篇幅比較長(zhǎng)狐蜕,先來(lái)個(gè)思維導(dǎo)圖預(yù)覽一下。 一卸夕、概述 1.計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)分層 2.TCP/IP 通信傳輸流 ...
    滌生_Woo閱讀 55,011評(píng)論 24 557
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理层释,服務(wù)發(fā)現(xiàn),斷路器快集,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • 原文鏈接:http://tech110.blog.51cto.com/438717/549764 Http的Cac...
    五鮮譜閱讀 766評(píng)論 0 2