首页 > 代码库 > HTTP POST, PUT PATCH
HTTP POST, PUT PATCH
- POST = 新增
- GET = 讀取
- PUT = 更新
- DELETE = 刪除
PUT 会在地址栏显示参数信息,不安全!
理解POST和PUT的区别,顺便提下RESTfu
这两个方法咋一看都可以更新资源,但是有本质区别的
具体定义可以百度,我这里就不贴了,光说我自己的理解
首先解释幂等,幂等是数学的一个用语,对于单个输入或者无输入的运算方法,如果每次都是同样的结果,则称其是幂等的
对于两个参数,如果传入值相等,结果也等于每个传入值,则称其为幂等的,如min(a,b)
POST
用于提交请求,可以更新或者创建资源,是非幂等的
举个例子,在我们的支付系统中,一个api的功能是创建收款金额二维码,它和金额相关,每个用户可以有多个二维码,如果连续调用则会创建新的二维码,这个时候就用POST
PUT
用于向指定的URI传送更新资源,是幂等的
还是那个例子,用户的账户二维码只和用户关联,而且是一一对应的关系,此时这个api就可以用PUT,因为每次调用它,都将刷新用户账户二维码
比如一个接口用于用户生成,接收的数据是用户名、密码等相关信息,则用POST
RESTful建议所有的URI都是对应资源,所以创建用户不应该理解为一个行为,在此将此接口命名为:
/user/creation
每次调用它都会新建一个用户(假定用户名可以重复)
而PUT方法更加关心一个具体资源对应的URI,比如更新当前用户信息,这里可以用PUT
/user/me/update
这里用me来指代当前用户,如果是针对更多用户适用的接口,可以考虑
/user/{uid}/update
注意多次调用同一接口,只要提交的数据一致,用户信息每次结果就会一致,即产生同样的结果:服务器端某个具体的资源得到了更新
当需要以更新的形式来修改某一具体资源的时候,如何判断用PUT还是POST呢?
很简单,如果该更新对应的URI多次调用的结果一致,则PUT
比如更新某个blog文章,因为该文章具有单一的具体URI,所以每次更新提交相同的内容,结果都一致
/blog/{document_id}/update
在每次更新提交相同的内容,最终的结果不一致的时候,用POST
举个很常见的例子,一个接口的功能是将当前余额减一个值,每次提交指定该值为100,接口如下
/amount/deduction
调用一次,你的余额-100,调用两次,余额-200
这个时候就用POST
RESTful的4种层次
Representational status transfer
个人理解为:表现形式的状态传递
1、只有一个接口交换xml来实现整个服务
目前我们的移动站点的服务就是类似的结构,我们有两个URI接口/mapp/lead和/msdk/safepay
2、每一个资源对应一个具体的URI,比1好维护,但是问题依然很明显,资源版本更新会引入时间戳维护,资源的获取和更新修改必须对应不同的URI
目前PC主站和移动站点的静态内容(包括html文件)都是这种形式
3、在2的基础上使用了http verb,每个URI可以有不同的动作,充分利用了http协议,所以自然居然http协议的完整优势,比如缓存和健壮性
HTML4.0只支持POST和GET,所以无论DELETE还是PUT操作,都用POST去模拟了
在WEB开发者看来,就是如果有数据变动,就用POST,如果没有,就用GET
所以目前中国用户来看,PC端实现RESTful很困难,只有移动端支持Html5的浏览器,才能让前端做出尝试
4、现在似乎更加无法实际应用,Hypemedia control,也就是RESTful的本意,合理的架构原理和以网络为基础的设计相结合,带来一个更加方便、功能强大的通信架构
这就有点虚无缥缈了,不过是一个努力的方向,想想看,以后要缴水费了,打开浏览器,输入我要缴水费,就自动定位+自动下单+自动付款+自动展示结果,完成整个缴水费的过程,这是多么方便的领悟!gwy要失业了有木有,那帮吃白饭做很简单的事情的,生产力发展第1个要淘汰的就是阻碍生产力发展的落后生产关系……
後來在設計 API only 的 Web service 時,常常搞不清楚到底要用 PUT 還是 POST,才發現我被 Rails 的鷹架範例誤導了(被框架框住想法了?),所謂的 PUT 其實也可以用到新增,而且還有一個蠻新的 HTTP Verb 叫做 PATCH,像 Github API 和 Rails 4 都開始採用。
PUT 比較正確的定義是 Replace (Create or Update),例如PUT /items/1
的意思是替換/items/1
,如果已經存在就替換,沒有就新增。PUT 必須包含items/1
的所有屬性資料。
但是這個行為似乎不怎麼好用,如果只是為了更新items/1
的其中一個屬性,就需要重傳所有items/1
的屬性也太浪費頻寬了,所以後來又有新的 PATCH Method 標準,可以用來做部分更新(Partial Update)。
用幾個 Ruby code 來舉例吧:
POST 新增:
# POST /items
def create
@item = Item.new
@item.attributes = { :name => params[:name],
:image => params[:image] }
@item.save
end
PUT 替換(新增或完整更新),此例中如果image參數沒有傳,會被更新成空:
# PUT /items/{id}
def replace
@item = Item.find_by_id(params[:id])
unless @item # if @item.nil?
@item = Item.new
@item.id = params[:id]
end
@item.attributes = { :name => params[:name],
:image => params[:image] }
@item.save
end
PATCH 部分更新,此例中如果image參數沒有傳,就不會被更新:
# PATCH /items/{id}
def patch
@item = Item.find(params[:id])
@item.attributes = params.slice(:name, :image)
@item.save
end
DELETE 刪除,此例中無論如何 items/1 最後都不存在了
# DELETE /items/{id}
def destroy
@item = Item.find_by_id(params[:id])
@item.destroy if @item
end
有時候拘泥於”語意”這件事情不容易想清楚設計 REST API 時要用哪一個 HTTP 方法,因為有時候不一定是CRUD的形式。我認為 9.1 Safe and Idempotent Methods 定義中的 “Idempotent” 特性蠻實用的。idempotent 的意思是如果相同的操作再執行第二遍第三遍,結果還是跟第一遍的結果一樣 (也就是說不管執行幾次,結果都跟只有執行一次一樣)。根據 HTTP 的規格,GET, HEAD, PUT 和 DELETE 是 idempotent,相同的 Request 再執行一次,結果還是一樣。只有 POST 和 PATCH 不是 idempotent,POST 再執行一遍,會再新增一筆資料,PATCH 則是有不能保證 idempotent 的可能性(徵求例子)。POST 和 PATCH 都不是 idempotent 的操作,這也是為什麼 Github API 裡用 POST 當做 PATCH 的相容取代方案。
另一個 HTTP Methods 特性是”Safe”,這比較簡單,只有 GET 和 HEAD 是 Safe 操作。Safe 特性會影響是否可以快取(POST/PUT/PATCH/DELETE 一定都不可以快取)。而 Idempotent 特性則是會影響可否 Retry (重試,反正結果一樣)。
SAFE? | IDEMPOTENT? | |
---|---|---|
GET | Y | Y |
POST | N | N |
PATCH | N | N |
PUT | N | Y |
DELETE | N | Y |
透過 Idempotent 的特性,有時候可以幫助你判斷該用哪一個 HTTP Methods。回到前面講 PUT 好像不太好用,例如以瀏覽器為主的 HTML 應用表單,要麻是 POST 新增資料,要麻就是用 PATCH 部分更新已經存在的資料(你不會希望用 PUT 修改個人資料的時候,每次都要重傳照片吧),因此比較少用到 PUT。這也是為什麼 Rails 4 把表單修改的 PUT 改成 PATCH Method,透過 Rails 鷹架產生出來的 update,其實符合的是 PATCH 行為而不是 PUT。
不過還是有一些我認為蠻適合用PUT的情境,例如訂閱東西該用POST還是PUT呢?
POST /subscriptions
# 還是
PUT /subscriptions/{something_id}
訂閱東西只有兩個狀態,”已訂閱”或”沒有訂閱”,這個訂閱的操作再重送幾次,還是”已訂閱”,所以我認為蠻符合 PUT 的 idempotent 特性。而對應的取消訂閱 API 想當然就是
DELETE /subscriptions/{something_id}
另外一個我覺得有趣又實用的 PUT 例子是,設計 API 给可以離線 offline 使用的行動裝置(例如iPhone)。支援 offline 產生的資料,通常會使用 UUID 來產生 ID,這樣就不需要透過中央伺服器管控 ID,方便裝置之間的同步。這樣的情境下,新增資料的 REST API 其實可以提供兩種:
POST /items # 參數帶 uuid=4937973d-e349-460a-a6ad-38625125b24a。如果不帶uuid則由server來產生uuid
# 和
PUT /items/4937973d-e349-460a-a6ad-38625125b24a
對行動裝置的 client 來說,用POST的問題在於離線環境的不穩定,有可能POST之後沒有收到回傳,因此行動裝置不確定有沒有同步成功,這時候要再重試(retry),但是用 POST 就爆炸了,因為 server 會再新建一筆 uuid 重複的資料。但是用 PUT 就沒有問題了,PUT 是 Idempotent 的操作,可以重送沒有關係 (可以再搭配 Conditional PUT 的機制,用 ETag 和 Last-Modified Headers 確保沒有覆蓋衝突)
如果是沒有 offline 需求的 client,例如第三方應用,那麼就可以用 POST /items 這個 API,交由 server 來產生 uuid。
HTTP POST, PUT PATCH