HTTP定義了與服務器交互的不同方法,最基本的方法有4種,分別是GET,POST,PUT,DELETE。URL全稱是資源描述符。我們可以這樣認為: 一個URL地址,它用于描述一個網絡上的資源,而HTTP中的GET,POST,PUT,DELETE就對應著對這個資源的 查,改,增,刪 4個操作。到這里,大家應該有個大概的了解了,GET一般用于獲取/查詢資源信息,而POST一般用于更新資源信息。那么,除了上面說的四種方法,HTTP還有其它方法么?其實HTTP中定義了以下幾種請求方法: - GET方法;
- POST方法;
- PUT方法;
- DELETE方法。
- HEAD方法;
- TRACE方法;
- OPTIONS方法;
Get是最常用的方法,通常用于請求服務器發(fā)送某個資源,而且應該是安全的和冪等的。 (1). 所謂安全是指該操作用于獲取信息而非修改信息。換句話說,GET 請求一般不應產生副作用。就是說,它僅僅是獲取資源信息,就像數據庫查詢一樣,不會修改和增加數據,不會影響資源的狀態(tài)。 注意:這里安全的含義僅僅是指是非修改信息。 (2). 冪等是指對同一個URL的多個請求應該返回同樣的結果。這里我再解釋一下冪等這個概念: 冪等(idempotent、idempotence)是一個數學或計算機學概念,常見于抽象代數中。 冪等有以下幾種定義: 對于單目運算,如果一個運算對于在范圍內的所有的一個數多次進行該運算所得的結果和進行一次該運算所得的結果是一樣的,那么我們就稱該運算是冪等的。比如絕對值運算就是一個例子,在實數集中,有abs(a)=abs(abs(a))。 對于雙目運算,則要求當參與運算的兩個值是等值的情況下,如果滿足運算結果與參與運算的兩個值相等,則稱該運算冪等,如求兩個數的最大值的函數,在實數集中便是冪等的 ,即max(x,x) = x。
POST方法向服務器提交數據,比如完成表單數據的提交,將數據提交給服務器處理。 PUT方法是讓服務器用請求的主體部分來創(chuàng)建一個由所請求的URL命名的新文檔;如果那個文檔存在的話,就用這個主體來代替它。 DELETE方法就是請求服務器刪除指定URL所對應的資源。但是,客戶端無法保證刪除操作一定會被執(zhí)行,因為HTTP規(guī)范允許服務器在不通知客戶端的情況下撤銷請求。 HEAD方法與GET方法的行為很類似,但服務器在響應中只返回實體的主體部分。這就允許客戶端在未獲取實際資源的情況下,對資源的首部進行檢查, 使用HEAD,我們可以更高效的完成以下工作: ①. 在不獲取資源的情況下,了解資源的一些信息,比如資源類型; ②. 通過查看響應中的狀態(tài)碼,可以確定資源是否存在; ③. 通過查看首部,測試資源是否被修改。 TRACE方法會在目的服務器端發(fā)起一個“回環(huán)”診斷,我們都知道,客戶端在發(fā)起一個請求時,這個請求可能要穿過防火墻、代理、網關、或者其它的一些應用程序。這中間的每個節(jié)點都可能會修改原始的HTTP請求,TRACE方法允許客戶端在最終將請求發(fā)送服務器時,它變成了什么樣子。由于有一個“回環(huán)”診斷,在請求最終到達服務器時,服務器會彈回一條TRACE響應,并在響應主體中攜帶它收到的原始請求報文的最終模樣。這樣客戶端就可以查看HTTP請求報文在發(fā)送的途中,是否被修改過了。 OPTIONS方法用于獲取當前URL所支持的方法。若請求成功,則它會在HTTP頭中包含一個名為“Allow”的頭,值是所支持的方法,如“GET, POST”。
GET和POST的區(qū)別: GET請求的數據會附在URL之后(就是把數據放置在HTTP協(xié)議頭中),以?分割URL和傳輸數據,參數之間以&相連,如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果數據是英文字母或數字,則原樣發(fā)送;如果是空格,轉換為+;如果是中文或其他字符,則直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX為該符號以16進制表示的ASCII碼值。而與之對應的,POST把提交的數據放置在HTTP包的包體中,文章最下面將會有代碼示例。 POST的安全性要比GET的安全性高。注意:這里所說的安全性和上面GET提到的“安全”不是同個概念。上面“安全”的含義僅僅是不作數據修改,而這里安全的含義是真正的Security的含義。比如:通過GET提交數據,用戶名和密碼將明文出現(xiàn)在URL上,因為:(1)登錄頁面有可能被瀏覽器緩存,(2)其他人查看瀏覽器的歷史紀錄,那么別人就可以拿到你的賬號和密碼了,除此之外,使用GET提交數據還可能會造成Cross-site request forgery攻擊(CSRF,跨站請求偽造,也被稱為:one click attack/session riding)。
例:銀行網站A,它以GET請求來完成銀行轉賬的操作,如:http://www./Transfer.php?toBankId=11&money=1000 危險網站B,它里面有一段HTML的代碼如下:< img src=http://www./Transfer.php?toBankId=11&money=1000/> 首先,你登錄了銀行網站A,然后訪問危險網站B,噢,這時你會發(fā)現(xiàn)你的銀行賬戶少了1000塊…… 為什么會這樣呢?原因是銀行網站A違反了HTTP規(guī)范,使用GET請求更新資源。在訪問危險網站B之前,你已經登錄了銀行網站A,而B中的< img …/>以GET的方式請求第三方資源(這里的第三方就是指銀行網站A了。原本這是一個合法的請求,但這里被不法分子利用了),所以你的瀏覽器會帶上你的銀行網站A的Cookie發(fā)出Get請求,去獲取資源“http://www./Transfer.php?toBankId=11&money=1000”,結果銀行網站服務器收到請求后,認為這是一個更新資源操作(轉賬操作),所以就立刻進行轉賬操作…… 為了杜絕上面的問題,銀行改用POST請求完成轉賬操作。
GET和POST的誤區(qū): 誤區(qū)一:POST可以比GET提交更多更長的數據? 由于使用GET方法提交數據時,數據會以&符號作為分隔符的形式,在URL后面添加需要提交的參數,有人會說,瀏覽器地址欄輸入的參數是有限的,而POST不用再地址欄輸入,所以POST就比GET可以提交更多的數據。難道真的是這樣的么? 而實際上,URL不存在參數上限的問題,HTTP協(xié)議規(guī)范沒有對URL長度進行限制。這個限制是特定的瀏覽器及服務器對它的限制。IE對URL長度的限制是2083字節(jié)(2K+35)。對于其他瀏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決于操作系統(tǒng)的支持。所以POST也是沒有大小長度限制的,HTTP協(xié)議規(guī)范也沒有進行大小限制。起限制作用的是服務器的處理能力??倸w一句話,這個限制是針對所有HTTP請求的,與GET、POST沒有多少關系。
注意: 上面大概說了一下HTTP規(guī)范中GET和POST的一些原理性的問題。但在實際的做的時候,很多人卻沒有按照HTTP規(guī)范去做,導致這個問題的原因有很多,比如說: 很多人貪方便,更新資源時用了GET,因為用POST必須要用到FORM(表單),這樣會麻煩一點。 對資源的增,刪,改,查操作,其實都可以通過GET/POST完成,不需要用到PUT和DELETE。 早期的Web MVC框架設計者們并沒有有意識地將URL當作抽象的資源來看待和設計,所以導致一個比較嚴重的問題是傳統(tǒng)的Web MVC框架基本上都只支持GET和POST兩種HTTP方法,而不支持PUT和DELETE方法。 大家都覺得使用GET很方便,畢竟使用POST要用到Form。但是使用GET方法時,瀏覽器會緩存你的地址等信息,留下歷史記錄和Cookie。而對于POST方法,則不會進行緩存。以后在開發(fā)中,一定要分清楚GET和POST的使用場合
創(chuàng)建和更新某個URL代表的資源的時候,是用HTTP的PUT還是POST: 摘抄自:http://www.cnblogs.com/shanyou/archive/2011/10/17/2215930.html 其實,用PUT還是POST,不是看這是創(chuàng)建還是更新資源的動作,這不是風格的問題,而是語義的問題。REST是一種風格,但是還是依賴于HTTP協(xié)議。在HTTP中,PUT被定義為idempotent的方法,POST則不是,這是一個很重要的區(qū)別。 “Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.” 上面的話就是說,如果一個方法重復執(zhí)行多次,產生的效果是一樣的,那就是idempotent的。 舉一個簡單的例子,假如由一個博客系統(tǒng)提供一個Web API,模式是 http://superblogging/blogs/post/{blog-name} 。很簡單,將{blog-name}替換為我們的blog名字,往這個URI發(fā)送一個HTTP PUT或者POST請求,HTTP的body部分就是博文,這是一個很簡單的REST API例子。我們應該用PUT方法還是POST方法?取決于這個REST服務的行為是否是idempotent的,假如我們發(fā)送兩個http://superblogging/blogs/post/Sample請求,服務器端是什么樣的行為?如果產生了兩個博客帖子,那就說明這個服務不是idempotent的,因為多次使用產生了副作用;如果后一個請求把第一個請求覆蓋掉了,那這個服務就是idempotent的。前一種情況,應該使用POST方法,后一種情況,應該使用PUT方法。 也許你會覺得這兩個方法的差別沒什么大不了的,用錯了也不會有什么問題,但是你的服務一放到internet上,如果不遵從HTTP協(xié)議的規(guī)范,就可能給自己帶來麻煩。比如,沒準Google Crawler也會訪問你的服務,如果讓一個不是indempotent的服務可以用indempotent的方法訪問,那么你服務器的狀態(tài)可能就會被Crawler修改,這是不應該發(fā)生的。
使用GET: @Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new AsyncTask() { //網絡加載是耗時操作,放在異步任務中 @Override protected Void doInBackground(String... params) { //相當于線程中的run方法,執(zhí)行后臺操作 try { URL u = new URL(params[0]); URLConnection conn = u.openConnection(); //獲取URL的互聯(lián)網連接 InputStream is = conn.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); //包裹到BufferedReader中 String line; while ((line = br.readLine()) != null) { System.out.println(line); } br.close(); isr.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } return null; } }.execute('http://apis./oil/region?key=3f73d0ea9d7c33d288fdc16f5257c1a5&format=2&city=%E5%8C%97%E4%BA%AC%E5%B8%82'); //execte(URL)使用GET方法時,填入的URL是帶有信息的,例子中使用的是聚合數據提供的加油站數據}
使用POST: @Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new AsyncTask() { @Override protected Void doInBackground(String... params) { //相當于線程中的run方法,執(zhí)行后臺操作 try { URL u = new URL(params[0]); HttpURLConnection conn = (HttpURLConnection) u.openConnection(); //使用POST,需將URLConnection轉換成HttpURLConnection //使用之前,需要先對conn進行配置 conn.setDoInput(true); conn.setDoOutput(true); //設置成true,conn才能向服務器輸出數據 conn.setRequestMethod('POST'); //請求方式設置為POST OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream()); //把數據傳到服務器 BufferedWriter bw = new BufferedWriter(osw); bw.write('key=3f73d0ea9d7c33d288fdc16f5257c1a5&format=2&city=%E5%8C%97%E4%BA%AC%E5%B8%82'); //要傳遞的數據 bw.flush(); InputStream is = conn.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { System.out.println(line); } br.close(); isr.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } return null; } }.execute('http://apis./oil/region'); }
上述兩段代碼獲取到的數據結果一樣,如下圖:
從圖片可以看出,我們已經成功獲取到數據
本文部分內容轉載自下面的博文,但是原博文的部分內容是不正確的,我對其做了總結和修改。因此讀者在前往原文觀看時,先擦亮眼睛哈,多看看,多瞧瞧。還有,如果我也錯了,請大家一定要指正: http://www./archives/806 http://www.cnblogs.com/shanyou/archive/2011/10/17/2215930.html http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html
|