談"http get和post的區別"

——如下內容若有各類問題,煩請指出,謝謝各位^_^——html

 

最基本的Java程序員面試題都有這個題java

——http get和post的區別?程序員

很多人大學還沒畢業就知道,就算不知道也會去搜,我記得我快畢業那會,簡單搜出來,排在前面的大概就這麼幾個區別:web

一、get用於獲取數據,post用於提交數據面試

二、get提交參數追加在url後面,post參數能夠經過http body提交ajax

三、get的url會有長度上的限制,則post的數據則能夠很是大spring

四、get提交信息明文顯示在url上,不夠安全,post提交的信息不會在url上顯示數據庫

五、get提交能夠被瀏覽器緩存,post不會被瀏覽器緩存apache

如今回頭總結下,發現本身快畢業哪會本身真是什麼都不知道啊,當時網上搜出來的這份東西就是有誤的啊,國內也是各類傳來傳去,錯誤的處處看獲得,都快成標準答案了。今年5月用netty http 些服務端程序時,調接口無心發現了原來get也可使用http body提交數據,抽空弄了下tomcat,發現也能夠啊。今天整理筆記看到了這裏,以爲有必要在博客上記錄一下,避免後來人繼續犯錯。json

一點一點的說 第1點:rfc2616說get方法用於獲取指定uri所表明的資源,應該設計成冪等的(其餘狀況不變時,屢次請求返回一樣的結果,差很少算是隻讀),不過在很長一段時間內,get方法都有「寫」功能,最簡單的例子就是/delete?id=1,而後很常見的就是jsonp形式的請求。 post方法該作什麼rfc2616說是叫服務器本身決定,現實中用post進行只讀操做的不少啊,一些提供http接口的數據庫都有post json進行只讀查詢的功能,post提交表單數據進行寫操做處處都是。 因此第一點這個,不怎麼好評價,如今的多數用途下第一點就是廢話,什麼意思都沒表達。不過如今RESTful炒得火熱,在RESTful的理念下,第一點差很少算是對的,不過RESTful任重道遠啊,比起如今只用get/post的http,畢竟實質性的功能沒有多大變化。 應該是從此很長一段時間內,get和post在第一點上基本沒區別,能用post實現的操做,基本上也能用get實現。

第2點:這一點是最坑的。 http沒明確規定什麼get/post方法要用什麼樣的方式傳輸數據,之因此出現第2點所說的狀況,緣由主要有兩點:瀏覽器設計、服務器設計,瀏覽器不支持get+httpbody是比較常見的,fiddler模擬請求時,若是是get,你填下面的body部分會紅色顯示,告訴你這樣很差,僅僅是很差,由於fiddler並無禁止get+body這種請求,但看得出它不建議你用這種方式的請求。

常見的servlet服務器,好比tomcat,默認不解析get的body部分,形成了get不能用body傳遞參數的現象。我寫了個簡單的例子,對比看下就知道是tomcat沒解析get的body,不是get自己不能使用body傳參。

 1 import java.io.IOException;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 
 5 import org.apache.commons.lang3.StringUtils;
 6 import org.springframework.stereotype.Controller;
 7 import org.springframework.web.bind.annotation.RequestMapping;
 8 import org.springframework.web.bind.annotation.ResponseBody;
 9 
10 @Controller
11 public class TestController {
12     private static final String NEW_LINE = StringUtils.CR + StringUtils.LF;
13 
14     @ResponseBody
15     @RequestMapping("/testLogin")
16     public String testLogin(HttpServletRequest req, String username, String pwd) throws IOException {
17         StringBuilder builder = new StringBuilder();
18         builder.append("req.method: ").append(req.getMethod()).append(NEW_LINE);
19         builder.append("req.queryString: ").append(req.getQueryString()).append(NEW_LINE);
20         int contentLength = req.getContentLength();
21         String body = null;
22         if (contentLength > 0) {
23             byte[] bytes = new byte[contentLength];
24             req.getInputStream().read(bytes);
25             body = new String(bytes, "UTF-8");
26         }
27         builder.append("req.body: ").append(body).append(NEW_LINE);
28         builder.append("req.params.username: ").append(req.getParameter("username")).append(NEW_LINE);
29         builder.append("req.params.pwd: ").append(req.getParameter("pwd")).append(NEW_LINE);
30         builder.append("username: ").append(username).append(NEW_LINE);
31         builder.append("pwd: ").append(pwd);
32         System.err.println(builder);
33         return builder.toString();
34     }
35 }

上面這個controller功能很簡單,就是打印請求參數,而且原樣返回,結果以下

上面張圖看出來,body部分能夠獲取獲得,可是tomcat沒有讀取了ServletInputStream中的body並解析body,形成req.getParam獲取不到對應的參數。

 

這張圖就是把方法換成POST,Request的其他一個字符都沒有變,能夠看到tomcat讀取了ServletInputStream中的body,並進行解析,形成了手動讀取ServletInputStream時流中沒有內容了,打印出來的req.body顯示無內容,req.getParam可以獲取到解析完成後對應的參數。

 

這張圖是普通的get+queryString,效果和post+body同樣。

 

對比上面三個結果就知道,get不是不能使用body傳參,只是瀏覽器和服務器進行了限制。瀏覽器的限制我不知道,這個研究得很少,tomcat的限制卻是能夠根據配置解除。

官方配置:http://tomcat.apache.org/tomcat-8.0-doc/config/http.html

具體就是這一項

默認是POST,也就是當Content-Type=application/x-www-form-urlencoded(提交Web表單時的標準數據傳輸格式,跟url傳參格式同樣,使用鍵值對,用&區分)時,只對post方法的body部分進行解析。把tomcat的server.xml中Http1.1的Connector配置上這項,就可以讓tomcat可以解析get的body部分,也就可以在tomcat上使用get+body的方式了。

上面這圖是修改配置後的結果,改完配置後get+body跟post+body功能同樣了。

第3點:這個跟第2點同樣,rfc2616中說了不對uri長度作限制,要求http可以實現無限長度的uri,無限長度的body。

原話是下面這個:

The HTTP protocol does not place any a priori limit on the length of a URI. Servers MUST be able to handle the URI of any resource they serve, and SHOULD be able to handle URIs of unbounded length if they provide GET-based forms that could generate such URIs. A server SHOULD return 414 (Request-URI Too Long) status if a URI is longer than the server can handle (see section 10.4.15).

Note: Servers ought to be cautious about depending on URI lengths above 255 bytes, because some older client or proxy implementations might not properly support these lengths.

rfc2616:http://www.ietf.org/rfc/rfc2616.txt

現實中就是太長的url沒什麼用,太長的body部分也不是很好,因此各類客戶端服務端的實現默認都有限制的這http種各個部分的長度。瀏覽器的我不清楚,服務端的http實現通常這個長度都是能夠配置的,netty http的HttpServerCodec默認是4K長度(整個請求行,uri是請求行裏面大頭),tomcat是用maxHttpHeaderSize來配置請求行和header部分的總長度;post長度,netty http默認是8K,tomcat默認是maxPostSize=2M,tomcat的配置設置成-1就是無限長度,可是這麼作在實際中一點意義沒有。

第4點:安全,這個扯得有點遠,也隨便扯扯。 不是別人一眼看不到的就是安全,不是別人一眼看獲得就不安全。http抓包很容易的,對路由器作點手腳就可以抓一堆移動設備的http請求包,移動設備的http請求你本身都一眼看不到,更不用說別人了,想要安全,仍是用https吧,大多數人看到了也沒用。

另外扯一句,Base64不是加密,用這個加密就是掩耳盜鈴啊。

 

第5點:關於http response的緩存

get被建議作成冪等的,緩存是頗有必要的,通常靜態資源的get請求都是會設置有效緩存時間的,這一點很容易想明白。不少時候緩存get在服務器和瀏覽器中是默認行爲,這對大量使用get請求的ajax不利,因此ajax請求通常會在請求url後加上一個隨機數,瀏覽器和服務器就認爲它是不一樣的get請求,不會緩存這個get請求。

 

關於post的緩存,如今的實際狀況是絕大多數瀏覽器都不支持post緩存。 
看了下rfc2616中關於post的response緩存的說明,發覺說得很混亂,它主要有兩個地方說了post的緩存。 
第一個是關於post方法的說明中(9.5小節): 
Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource. 
這段話也就是說,是否緩存post的response,是根據http請求頭中的Cache-Control、Expires決定的。

 

還有一個位置,13.10小節(13節是專門說緩存的): 
In this section, the phrase 「invalidate an entity」 means that the cache will either remove all instances of that entity from its storage, or will mark these as 「invalid」 and in need of a mandatory revalidation before they can be returned in response to a subsequent request. 
Some HTTP methods MUST cause a cache to invalidate an entity. This is either the entity referred to by the Request-URI, or by the Location or Content-Location headers (if present). These methods are: 
- PUT 
- DELETE 
- POST 
這段話是說 PUT DELETE POST 應該讓緩存無效uri表明的實體——刪除該實體的全部實例存儲,或將這些標記爲「無效」並須要強制性從新驗證,而後才能返回以響應後續操做請求。

我以爲rfc2616中的兩處規定有衝突啊,都說叫POST無效緩存,那還緩存毛線啊!
stackoverflow上有個問題討論這個,感受也沒說明白:http://stackoverflow.com/questions/626057/is-it-possible-to-cache-post-methods-in-http

 

後來看了下rfc7231,它是對rfc2616的補充說明,其中關於POST的緩存說明以下: 
Responses to POST requests are only cacheable when they include explicit freshness information (see Section 4.2.1 of [RFC7234]). However, POST caching is not widely implemented. For cases where an origin server wishes the client to be able to cache the result of a POST in a way that can be reused by a later GET, the origin server MAY send a 200 (OK) response containing the result and a Content-Location header field that has the same value as the POST’s effective request URI (Section 3.1.4.2). 
這段話說POST的resp在某些條件下能夠被緩存,可是客戶端不多實現這個功能,具體是什麼條件,在rfc7234這篇專門說http緩存中說了,不過我看得不是很明白。

 

總之根據上面幾個rfc的意思,POST的resp其實是容許被緩存的,可是通常都不實現這個。 
瀏覽器不實現是有道理的,現實中POST大多執行的是寫操做,緩存寫操做結果無心義,還有POST能夠說是登陸/支付這類功能的標準方法了,雙方交流的是敏感數據,緩存這類數據的安全隱患很大。

 

總結:工做也有些時間了,我也不是一個純小白了,瞭解了一些方法和途徑,該多靠本身的力量去弄懂些東西。還有,這個問題一路走來也是深有感觸啊,網上搜到的東西不必定正確,排名靠前的也不必定是最真的,但確是那些初學者最能依賴的。像我這種工做的時候本身搗鼓無心間發現的還算幸運啊,畢竟這個問題,對於不少人,畢業的時候就定型了,可能會一生錯下去。

 

——以上內容若有各類問題,煩請指出,謝謝各位^_^——

相關文章
相關標籤/搜索