一、三者的對比
HttpClient:代碼復雜,還得操心資源回收等。代碼很復雜,冗余代碼多,不建議直接使用。 RestTemplate: 是 Spring 提供的用于訪問Rest服務的客戶端, RestTemplate 提供了多種便捷訪問遠程Http服務的方法,能夠大大提高客戶端的編寫效率。 okhttp:OkHttp是一個高效的HTTP客戶端,允許所有同一個主機地址的請求共享同一個socket連接;連接池減少請求延時;透明的GZIP壓縮減少響應數據的大小;緩存響應內容,避免一些完全重復的請求
二、HttpClient的使用
HttpClient是Apache Jakarta Common下的子項目,用來提供高效的、最新的、功能豐富的支持HTTP協議的客戶端編程工具包,并且它支持HTTP協議最新的版本和建議。HttpClient已經應用在很多的項目中,比如Apache Jakarta上很著名的另外兩個開源項目Cactus和HTMLUnit都使用了HttpClient。
使用HttpClient發送請求、接收響應很簡單,一般需要如下幾步即可。
創建HttpClient對象。 創建請求方法的實例,并指定請求URL。如果需要發送GET請求,創建HttpGet對象;如果需要發送POST請求,創建HttpPost對象。 如果需要發送請求參數,可調用HttpGet、HttpPost共同的setParams(HetpParams params)方法來添加請求參數;對于HttpPost對象而言,也可調用setEntity(HttpEntity entity)方法來設置請求參數。 調用HttpClient對象的execute(HttpUriRequest request)發送請求,該方法返回一個HttpResponse。 調用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調用HttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內容。程序可通過該對象獲取服務器的響應內容。 釋放連接。無論執行方法是否成功,都必須釋放連接
< dependency> < groupId> org.apache.httpcomponents
</ groupId> < artifactId> httpclient
</ artifactId> < version> 4.5.13
</ version> </ dependency> < dependency> < groupId> com.fasterxml.jackson.core
</ groupId> < artifactId> jackson-databind
</ artifactId> < version> 2.13.0
</ version> </ dependency>
public class HttpClientExample_get { public static void main ( String [ ] args
) throws IOException { HttpGet request
= new HttpGet ( "https://httpbin.org/get" ) ; request
. addHeader ( "custom-key" , "test" ) ; request
. addHeader ( HttpHeaders . USER_AGENT
, "Googlebot" ) ; try ( CloseableHttpClient httpClient
= HttpClients . createDefault ( ) ; CloseableHttpResponse response
= httpClient
. execute ( request
) ) { System . out
. println ( response
. getProtocolVersion ( ) ) ; System . out
. println ( response
. getStatusLine ( ) . getStatusCode ( ) ) ; System . out
. println ( response
. getStatusLine ( ) . getReasonPhrase ( ) ) ; System . out
. println ( response
. getStatusLine ( ) . toString ( ) ) ; HttpEntity entity
= response
. getEntity ( ) ; if ( entity
!= null ) { String result
= EntityUtils . toString ( entity
) ; System . out
. println ( result
) ; } } }
}
public class HttpClientExample_post { public static void main ( String [ ] args
) { try { String result
= sendPOST ( "https://httpbin.org/post" ) ; System . out
. println ( result
) ; } catch ( IOException e
) { e
. printStackTrace ( ) ; } } private static String sendPOST ( String url
) throws IOException { String result
= "" ; HttpPost post
= new HttpPost ( url
) ; List < NameValuePair > urlParameters
= new ArrayList < > ( ) ; urlParameters
. add ( new BasicNameValuePair ( "username" , "abc" ) ) ; urlParameters
. add ( new BasicNameValuePair ( "password" , "123" ) ) ; urlParameters
. add ( new BasicNameValuePair ( "custom" , "secret" ) ) ; post
. setEntity ( new UrlEncodedFormEntity ( urlParameters
) ) ; try ( CloseableHttpClient httpClient
= HttpClients . createDefault ( ) ; CloseableHttpResponse response
= httpClient
. execute ( post
) ) { result
= EntityUtils . toString ( response
. getEntity ( ) ) ; } return result
; }
}
public class HttpClientExample_JsonPost { public static void main ( String [ ] args
) { try { String result
= sendPOST ( "https://httpbin.org/post" ) ; System . out
. println ( result
) ; } catch ( IOException e
) { e
. printStackTrace ( ) ; } } private static String sendPOST ( String url
) throws IOException { String result
= "" ; HttpPost post
= new HttpPost ( url
) ; String json
= "{" + "\"name\":\"mkyong\"," + "\"notes\":\"hello\"" + "}" ; post
. setEntity ( new StringEntity ( json
) ) ; try ( CloseableHttpClient httpClient
= HttpClients . createDefault ( ) ; CloseableHttpResponse response
= httpClient
. execute ( post
) ) { result
= EntityUtils . toString ( response
. getEntity ( ) ) ; } return result
; }
}
public class HttpClientAuthentication { public static void main ( String [ ] args
) throws IOException { HttpGet request
= new HttpGet ( "http://localhost:8080/books" ) ; CredentialsProvider provider
= new BasicCredentialsProvider ( ) ; provider
. setCredentials ( AuthScope . ANY
, new UsernamePasswordCredentials ( "user" , "password" ) ) ; try ( CloseableHttpClient httpClient
= HttpClientBuilder . create ( ) . setDefaultCredentialsProvider ( provider
) . build ( ) ; CloseableHttpResponse response
= httpClient
. execute ( request
) ) { System . out
. println ( response
. getStatusLine ( ) . getStatusCode ( ) ) ; HttpEntity entity
= response
. getEntity ( ) ; if ( entity
!= null ) { String result
= EntityUtils . toString ( entity
) ; System . out
. println ( result
) ; } } }
}
參考文章 參考文章
三、OKHttp的使用
提供了對 HTTP/2 和 SPDY 的支持,這使得對同一個主機發出的所有請求都可以共享相同的套接字連接 如果 HTTP/2 和 SPDY 不可用,OkHttp會使用連接池來復用連接以提高效率 提供了對 GZIP 的默認支持來降低傳輸內容的大小 提供了對 HTTP 響應的緩存機制,可以避免不必要的網絡請求 當網絡出現問題時,OkHttp 會自動重試一個主機的多個 IP 地址
Requests(請求): 每一個HTTP請求中都應該包含一個URL,一個GET或POST方法以及Header或其他參數,當然還可以含特定內容類型的數據流。 Responses(響應): 響應則包含一個回復代碼(200代表成功,404代表未找到),Header和定制可選的body。
pom.xml
< dependency> < groupId> com.squareup.okhttp3
</ groupId> < artifactId> okhttp
</ artifactId> < version> 4.9.1
</ version>
</ dependency>
創建OkHttpClient實例
簡單來說,通過OkHttpClient可以發送一個Http請求,并讀取該Http請求的響應,它是一個生產Call的工廠。此外,受益于一個共享的響應緩存/線程池/復用的連接等因素,絕大多數應用使用一個OkHttpClient實例,便可以滿足整個應用的Http請求。
三種創建實例的方法:
創建一個默認配置OkHttpClient,可以使用默認的構造函數。 通過new OkHttpClient.Builder()方法來一步一步配置一個OkHttpClient實例。 如果要求使用現有的實例,可以通過newBuilder()方法來進行構造。
OkHttpClient client
= new OkHttpClient ( ) ;
OkHttpClient clientWith30sTimeout
= client. Builder( ) . readTimeout ( 30 , TimeUnit . SECONDS
) . build ( ) ;
OkHttpClient client
= client
. newBuilder ( ) . build ( ) ;
看一下OkHttpClient的源碼,會發現緩存/代理等等需求,一應俱全的按照類封裝到了Builder中。
Dispatcher dispatcher
;
Proxy proxy
;
List < Protocol > protocols
;
List < ConnectionSpec > connectionSpecs
;
final List < Interceptor > interceptors
= new ArrayList < > ( ) ;
final List < Interceptor > networkInterceptors
= new ArrayList < > ( ) ;
ProxySelector proxySelector
;
CookieJar cookieJar
;
Cache cache
;
InternalCache internalCache
;
SocketFactory socketFactory
;
SSLSocketFactory sslSocketFactory
;
HostnameVerifier hostnameVerifier
;
CertificatePinner certificatePinner
;
Authenticator proxyAuthenticator
;
Authenticator authenticator
;
ConnectionPool connectionPool
;
Dns dns
;
boolean followSslRedirects
;
boolean followRedirects
;
boolean retryOnConnectionFailure
;
int connectTimeout
;
int readTimeout
;
int writeTimeout
;
okhttp–get
OkHttpClient client
= new OkHttpClient ( ) ;
String run ( String url
) throws IOException { Request request
= new Request. Builder ( ) . url ( url
) . build ( ) ; Response response
= client
. newCall ( request
) . execute ( ) ; return response
. body ( ) . string ( ) ;
}
簡單看一下Request類,可以發現它代表一個Http請求,需要注意的是Request一旦build()之后,便不可修改。主要通過new Request.Builder()來一步一步構造的??匆幌翨uilder的代碼。
public Builder ( ) { this . method
= "GET" ; this . headers
= new Headers. Builder ( ) ;
}
此外還創建了頭信息。Headers類中是通過List<String> namesAndValues = new ArrayList<>(20),來存放頭信息的,一開始我也很納悶,頭信息都是一對一對的為什么要用List,看一下源碼發現,在存取的時候都是將索引+2或者-2。并且頭信息可以存在多個相同的Key信息。
跟到newCall()方法中發現,又使用OkHttpClient實例和Request的實例,一起構造了一個RealCall的實例。RealCall類簡單做了一個托管并通過Dispather類對請求進行分發和執行,實際開啟線程發起請求的方法就在這個類中。隨后又調用execute()方法,拿到了一個響應。這個execute()方法,實際上執行的就是RealCall中的execute()方法,最后調用了Dispatcher的execute()方法。
Response代表一個Http的響應,這個類的實例不可修改。
一個簡單的Get請求和說明就結束了
okhttp–post
public static final MediaType JSON
= MediaType . parse ( "application/json; charset=utf-8" ) ;
OkHttpClient client
= new OkHttpClient ( ) ;
String post ( String url
, String json
) throws IOException { RequestBody body
= RequestBody . create ( JSON
, json
) ; Request request
= new Request. Builder ( ) . url ( url
) . post ( body
) . build ( ) ; Response response
= client
. newCall ( request
) . execute ( ) ; return response
. body ( ) . string ( ) ;
}
MediaType用于描述Http請求和響應體的內容類型,也就是Content-Type。一次請求就是向目標服務器發送一串文本。什么樣的文本?有下面結構的文本。
POST
/ meme
. php
/ home
/ user
/ login HTTP
/ 1.1
Host : 114.215 .86 .90
Cache - Control : no
- cache
Postman - Token : bd243d6b
- da03
- 902f - 0 a2c
- 8e9377f 6f 6 ed
Content - Type : application
/ x
- www
- form
- urlencodedtel
= 13637829200 & password
= 123456
例如,MediaType.parse(“application/json; charset=utf-8”);這個就帶表請求體的類型為JSON格式的。定義好數據類型,還要將其變為請求體,最后通過post()方法,隨請求一并發出。
OkHttp也可以通過POST方式把鍵值對數據傳送到服務器
OkHttpClient client
= new OkHttpClient ( ) ;
String post ( String url
, String json
) throws IOException { RequestBody formBody
= new FormBody. Builder ( ) . add ( "platform" , "android" ) . add ( "name" , "bug" ) . add ( "subject" , "XXXXXXXXXXXXXXX" ) . build ( ) ; Request request
= new Request. Builder ( ) . url ( url
) . post ( formBody
) . build ( ) ; Response response
= client
. newCall ( request
) . execute ( ) ; if ( response
. isSuccessful ( ) ) { return response
. body ( ) . string ( ) ; } else { throw new IOException ( "Unexpected code " + response
) ; }
}
HTTP頭部的設置和讀取
HTTP 頭的數據結構是 Map<String, List<String>>類型。也就是說,對于每個 HTTP 頭,可能有多個值。但是大部分 HTTP 頭都只有一個值,只有少部分 HTTP 頭允許多個值。至于name的取值說明,可以查看這個請求頭大全。
OkHttp的處理方式是:
使用header(name,value)來設置HTTP頭的唯一值,如果請求中已經存在響應的信息那么直接替換掉。 使用addHeader(name,value)來補充新值,如果請求頭中已經存在name的name-value,那么還會繼續添加,請求頭中便會存在多個name相同而value不同的“鍵值對”。 使用header(name)讀取唯一值或多個值的最后一個值 使用headers(name)獲取所有值
OkHttpClient client
= new OkHttpClient ( ) ;
Request request
= new Request. Builder ( ) . url ( "https://github.com" ) . header ( "User-Agent" , "My super agent" ) . addHeader ( "Accept" , "text/html" ) . build ( ) ;
Response response
= client
. newCall ( request
) . execute ( ) ;
if ( ! response
. isSuccessful ( ) ) { throw new IOException ( "服務器端錯誤: " + response
) ;
}
System . out
. println ( response
. header ( "Server" ) ) ;
System . out
. println ( response
. headers ( "Set-Cookie" ) ) ;
參考文章
四、RestTemplate的使用
RestTemplate 是從 Spring3.0 開始支持的一個 HTTP 請求工具,它提供了常見的REST請求方案的模版,例如 GET 請求、POST 請求、PUT 請求、DELETE 請求以及一些通用的請求執行方法 exchange 以及 execute。RestTemplate 繼承自 InterceptingHttpAccessor 并且實現了 RestOperations 接口,其中 RestOperations 接口定義了基本的 RESTful 操作,這些操作在 RestTemplate 中都得到了實現。
< dependency> < groupId> org.springframework.boot
</ groupId> < artifactId> spring-boot-starter-web
</ artifactId>
</ dependency>
RestTemplate restTemplate
= new RestTemplate ( ) ;
RestTemplate restTemplate
= new RestTemplate ( new HttpComponentsClientHttpRequestFactory ( ) ) ;
RestTemplate restTemplate
= new RestTemplate ( new OkHttp3ClientHttpRequestFactory ( ) ) ;
當然如果想使用OkHttp的話也得引入相應的jar包
默認情況下RestTemplate自動幫我們注冊了一組HttpMessageConverter用來處理一些不同的contentType的請求。如果現有的轉換器不能滿足你的需求,你還可以實現org.springframework.http.converter.HttpMessageConverter接口自己寫一個。詳情參考官方api。其他相關的配置,也可以在官方文檔中查看。當然,對于一個請求來說,超期時間,請求連接時間等都是必不可少的參數,為了更好的適應業務需求,所以可以自己修改restTemplate的配置。
RestTemplate提供了六種常用的HTTP方法實現遠程服務調用,RestTemplate的方法名遵循一定的命名規范,第一部分表示用哪種HTTP方法調用(get,post),第二部分表示返回類型。
getForObject – 發送GET請求,將HTTP response轉換成一個指定的object對象
postForEntity –發送POST請求,將給定的對象封裝到HTTP請求體,返回類型是一個HttpEntity對象(包含響應數據和響應頭)
每個HTTP方法對應的RestTemplate方法都有3種。其中2種的url參數為字符串,URI參數變量分別是Object數組和Map,第3種使用URI類型作為參數
exchange 和execute方法比上面列出的其它方法(如getForObject、postForEntity等)使用范圍更廣,允許調用者指定HTTP請求的方法(GET、POST、PUT等),并且可以支持像HTTP PATCH(部分更新)。
做好了準備工作,先來看使用 RestTemplate 發送 GET 請求。在 RestTemplate 中,和 GET 請求相關的方法有如下幾個:
public class TemplateGet { public static void main ( String [ ] args
) throws UnsupportedEncodingException { RequestConfig config
= RequestConfig . custom ( ) . setConnectionRequestTimeout ( 10000 ) . setConnectTimeout ( 10000 ) . setSocketTimeout ( 30000 ) . build ( ) ; HttpClientBuilder builder
= HttpClientBuilder . create ( ) . setDefaultRequestConfig ( config
) . setRetryHandler ( new DefaultHttpRequestRetryHandler ( 3 , false ) ) ; HttpClient httpClient
= builder
. build ( ) ; ClientHttpRequestFactory requestFactory
= new HttpComponentsClientHttpRequestFactory ( httpClient
) ; RestTemplate restTemplate
= new RestTemplate ( requestFactory
) ; String forObject
= restTemplate
. getForObject ( "https://httpbin.org/get" , String . class ) ; ResponseEntity < String > forEntity
= restTemplate
. getForEntity ( "https://httpbin.org/get" , String . class ) ; ResponseEntity < String > responseEntity
= restTemplate
. exchange ( "https://httpbin.org/get" , HttpMethod . GET
, null , String . class ) ; System . out
. println ( "響應頭--start" ) ; forEntity
. getHeaders ( ) . entrySet ( ) . forEach ( System . out
:: println ) ; System . out
. println ( "響應頭--end" ) ; assert forObject
!= null ; byte [ ] bytes
= forObject
. getBytes ( StandardCharsets . UTF_8
) ; String res
= new String ( bytes
, StandardCharsets . UTF_8
) ; System . out
. println ( res
) ; System . out
. println ( forEntity
. getBody ( ) ) ; System . out
. println ( responseEntity
. getBody ( ) ) ; }
}
json形式
public class TemplatePostJson { public static void main ( String [ ] args
) { RequestConfig config
= RequestConfig . custom ( ) . setConnectionRequestTimeout ( 10000 ) . setConnectTimeout ( 10000 ) . setSocketTimeout ( 30000 ) . build ( ) ; HttpClientBuilder builder
= HttpClientBuilder . create ( ) . setDefaultRequestConfig ( config
) . setRetryHandler ( new DefaultHttpRequestRetryHandler ( 3 , false ) ) ; HttpClient httpClient
= builder
. build ( ) ; ClientHttpRequestFactory requestFactory
= new HttpComponentsClientHttpRequestFactory ( httpClient
) ; RestTemplate restTemplate
= new RestTemplate ( requestFactory
) ; String url
= "https://httpbin.org/post" ; String json
= "{" + "\"name\":\"mkyong\"," + "\"notes\":\"hello\"" + "}" ; HttpHeaders headers
= new HttpHeaders ( ) ; headers
. setContentType ( MediaType . APPLICATION_JSON
) ; headers
. set ( "CustomHeader" , "myCustom" ) ; HttpEntity < String > httpEntity
= new HttpEntity < > ( json
, headers
) ; ResponseEntity < String > responseEntity
= restTemplate
. exchange ( url
, HttpMethod . POST
, httpEntity
, String . class ) ; String s
= restTemplate
. postForObject ( url
, httpEntity
, String . class ) ; ResponseEntity < String > stringResponseEntity
= restTemplate
. postForEntity ( url
, httpEntity
, String . class ) ; assert s
!= null ; byte [ ] bytes
= s
. getBytes ( StandardCharsets . UTF_8
) ; String body
= new String ( bytes
, StandardCharsets . UTF_8
) ; System . out
. println ( body
) ; System . out
. println ( responseEntity
. getBody ( ) ) ; System . out
. println ( "響應頭--start" ) ; stringResponseEntity
. getHeaders ( ) . entrySet ( ) . forEach ( System . out
:: println ) ; System . out
. println ( "響應頭--end" ) ; }
}
map(表單)形式
public class TemplatePostMap { public static void main ( String [ ] args
) { RequestConfig config
= RequestConfig . custom ( ) . setConnectionRequestTimeout ( 10000 ) . setConnectTimeout ( 10000 ) . setSocketTimeout ( 30000 ) . build ( ) ; HttpClientBuilder builder
= HttpClientBuilder . create ( ) . setDefaultRequestConfig ( config
) . setRetryHandler ( new DefaultHttpRequestRetryHandler ( 3 , false ) ) ; HttpClient httpClient
= builder
. build ( ) ; ClientHttpRequestFactory requestFactory
= new HttpComponentsClientHttpRequestFactory ( httpClient
) ; RestTemplate restTemplate
= new RestTemplate ( requestFactory
) ; String url
= "https://httpbin.org/post" ; MultiValueMap < Object , Object > map
= new LinkedMultiValueMap < > ( ) ; map
. add ( "aa" , "aa" ) ; map
. add ( "bb" , "bb" ) ; map
. add ( "cc" , "cc" ) ; HttpHeaders headers
= new HttpHeaders ( ) ; headers
. setContentType ( MediaType . APPLICATION_JSON
) ; headers
. set ( "CustomHeader" , "myCustom" ) ; HttpEntity < MultiValueMap < Object , Object > > httpEntity
= new HttpEntity < > ( map
, headers
) ; ResponseEntity < String > responseEntity
= restTemplate
. exchange ( url
, HttpMethod . POST
, httpEntity
, String . class ) ; String s
= restTemplate
. postForObject ( url
, httpEntity
, String . class ) ; ResponseEntity < String > stringResponseEntity
= restTemplate
. postForEntity ( url
, httpEntity
, String . class ) ; assert s
!= null ; byte [ ] bytes
= s
. getBytes ( StandardCharsets . UTF_8
) ; String body
= new String ( bytes
, StandardCharsets . UTF_8
) ; System . out
. println ( body
) ; System . out
. println ( responseEntity
. getBody ( ) ) ; System . out
. println ( "響應頭--start" ) ; stringResponseEntity
. getHeaders ( ) . entrySet ( ) . forEach ( System . out
:: println ) ; System . out
. println ( "響應頭--end" ) ; }
}
其實,如果參數是一個 MultiValueMap 的實例,則以 key/value 的形式發送,如果是一個普通對象,則會被轉成 json 發送。
postForLocation 方法的返回值是一個 URL 對象,因為 POST 請求一般用來添加數據,有的時候需要將剛剛添加成功的數據的 URL 返回來,此時就可以使用這個方法,一個常見的使用場景如用戶注冊功能,用戶注冊成功之后,可能就自動跳轉到登錄頁面了,此時就可以使用該方法。
只要將 GET 請求和 POST 請求搞定了,接下來 PUT 請求就會容易很多了,PUT 請求本身方法也比較少,只有三個,如下: 這三個重載的方法其參數其實和 POST 是一樣的,可以用 key/value 的形式傳參,也可以用 JSON 的形式傳參,無論哪種方式,都是沒有返回值的
和 PUT 請求一樣,DELETE 請求也是比較簡單的,只有三個方法,如下:
不同于 POST 和 PUT ,DELETE 請求的參數只能在地址欄傳送,可以是直接放在路徑中,也可以用 key/value 的形式傳遞,當然,這里也是沒有返回值的。
public class TemplateDeleteJson { public static void main ( String [ ] args
) { RequestConfig config
= RequestConfig . custom ( ) . setConnectionRequestTimeout ( 10000 ) . setConnectTimeout ( 10000 ) . setSocketTimeout ( 30000 ) . build ( ) ; HttpClientBuilder builder
= HttpClientBuilder . create ( ) . setDefaultRequestConfig ( config
) . setRetryHandler ( new DefaultHttpRequestRetryHandler ( 3 , false ) ) ; HttpClient httpClient
= builder
. build ( ) ; ClientHttpRequestFactory requestFactory
= new HttpComponentsClientHttpRequestFactory ( httpClient
) ; RestTemplate restTemplate
= new RestTemplate ( requestFactory
) ; String url
= "https://httpbin.org/delete" ; String json
= "{" + "\"name\":\"mkyong\"," + "\"notes\":\"hello\"" + "}" ; HttpHeaders headers
= new HttpHeaders ( ) ; headers
. setContentType ( MediaType . APPLICATION_JSON
) ; headers
. set ( "CustomHeader" , "myCustom" ) ; HttpEntity < String > httpEntity
= new HttpEntity < > ( json
, headers
) ; restTemplate
. delete ( url
, httpEntity
) ; }
}
參考文章 參考文章 參考文章
總結
以上是生活随笔 為你收集整理的HttpClient、OKhttp、RestTemplate对比 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。