1、什么是REST?
REST(RepresentationalState Transfer)是Roy Fielding 提出的一個描述互聯系統架構風格的名詞。REST定義了一組體系架構原則,您可以根據這些原則設計以系統資源為中心的Web 服務,包括使用不同語言編寫的客戶端如何通過 HTTP處理和傳輸資源狀態。
為什么稱為 REST?Web本質上由各種各樣的資源組成,資源由URI 唯一標識。瀏覽器(或者任何其它類似於瀏覽器的應用程序)將展示出該資源的一種表現方式,或者一種表現狀態。如果用戶在該頁面中定向到指向其它資源的鏈接,則將訪問該資源,並表現出它的狀態。這意味着客戶端應用程序隨着每個資源表現狀態的不同而發生狀態轉移,也即所謂REST。
2、REST成熟度的四個層次
第一個層次(Level0)的Web 服務只是使用 HTTP 作為傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形 式。SOAP和 XML-RPC都屬於此類。
第二個層次(Level1)的Web 服務引入了資源的概念。每個資源有對應的標識符和表達。
第三個層次(Level2)的Web 服務使用不同的 HTTP 方法來進行不同的操作,並且使用HTTP 狀態碼來表示不同的結果。如 HTTPGET 方法來獲取資源,HTTPDELETE 方法來刪除資源。
第四個層次(Level3)的Web 服務使用 HATEOAS。在資源的表達中包含了鏈接信息。客戶端可以根據鏈接來發現可以執行的動作。
其中第三個層次建立了創建、讀取、更新和刪除(create,read, update, and delete,CRUD)操作與 HTTP方法之間的一對一映射。根據此映射:
(1)若要在服務器上創建資源,應該使用POST 方法。
(2)若要檢索某個資源,應該使用GET 方法。
(3)若要更改資源狀態或對其進行更新,應該使用PUT 方法。
(4)若要刪除某個資源,應該使用DELETE 方法。
3、HTTP請求的方法
(1)GET:通過請求URI得到資源
(2)POST:用於添加新的內容
(3)PUT:用於修改某個內容,若不存在則添加
(4)DELETE:刪除某個內容
(5)OPTIONS :詢問可以執行哪些方法
(6)HEAD :類似於GET, 但是不返回body信息,用於檢查對象是否存在,以及得到對象的元數據
(7)CONNECT :用於代理進行傳輸,如使用SSL
(8)TRACE:用於遠程診斷服務器
4、HTTP請求的狀態碼
(1)成功Successful2xx:此類狀態碼標識客戶端的請求被成功接收、理解並接受。常見如200(OK)、204(NoContent)。
(2)重定向Redirection3xx:這個類別的狀態碼標識用戶代理要做出進一步的動作來完成請求。常見如301(MovedPermanently)、302(MovedTemprarily)。
(3)客戶端錯誤Client Error 4xx:4xx類別的狀態碼是當客戶端象是出錯的時使用的。常見如400(BadRequest)、401(Unauthorized)、403(Forbidden)、404(NotFound)。
(4)服務器錯誤Server Error 5xx:響應狀態碼以5開頭表示服務器知道自己出錯或者沒有能力執行請求。常見如500(InternalServer Error)、502(BadGateway)、504(GatewayTimeout)。
附HTTP1.1的標准簡介:http://blog.chinaunix.net/uid-9188830-id-2007021.html
5、RestTemplate
5.1 簡介
Spring'scentral class for synchronous client-side HTTP access.It simplifies communication with HTTPservers, and enforces RESTful principles. Ithandles HTTP connections, leaving application code to provide URLs(with possible template variables) andextract results.
簡單說就是:簡化了發起HTTP請求以及處理響應的過程,並且支持REST。為什么說簡化了呢?
來看兩種實現方式
(1)使用java.net包下的URLConnection建立連接
String result= ""; BufferedReaderin = null; try { String urlNameString= url +"?" + param; URL realUrl= new URL(urlNameString); // 打開和URL之間的連接 URLConnectionconnection = realUrl.openConnection(); // 設置通用的請求屬性 connection.setRequestProperty("accept","*/*"); connection.setRequestProperty("connection","Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立實際的連接 connection.connect(); // 獲取所有響應頭字段 Map<String,List<String>> map = connection.getHeaderFields(); // 遍歷所有的響應頭字段 for(String key : map.keySet()) { System.out.println(key+ "--->" + map.get(key)); } // 定義 BufferedReader輸入流來讀取URL的響應 in =new BufferedReader(newInputStreamReader( connection.getInputStream())); String line; while ((line = in.readLine())!= null) { result += line; } } catch (Exception e) { … } // 使用finally塊來關閉輸入流 finally{ // 關閉流 }
(2)使用RestTempalte
ResponseEntity<SsoUrlPrm>result = restTemplate.getForEntity(requestPathUrl,SsoUrlPrm.class);
5.2 對外開放的接口
(1)
DELETE | delete |
GET | getForObject |
getForEntity | |
HEAD | headForHeaders |
OPTIONS | optionsForAllow |
POST | postForLocation |
postForObject | |
PUT | put |
any | exchange |
execute |
(2)每一個小類又分三種,這三種有什么區別?
這是因為,String類型的URI支持占位符。比如:
restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}",String.class,"42", "21");
那么最終訪問的URI為:http://example.com/hotels/42/bookings/21
但是String有一個小缺陷:String形式的URI會被URL編碼兩次(URL encode請自行百度),這就要求服務器在獲取URI中的參數時主動進行一次解碼,但如果服務的提供者不這么做呢?
這時就需要使用不會使用任何編碼的java.net.URI
PS:參數‘Class<T> responseType’定義了返回數據的類型。
(3)Exchange
>允許調用者指定HTTP請求的方法(GET,POST,PUT等)
>可以在請求中增加body以及頭信息,其內容通過參數‘HttpEntity<?>requestEntity’描述
>exchange支持‘含參數的類型’(即泛型類)作為返回類型,該特性通過‘ParameterizedTypeReference<T>responseType’描述。比如:
List<String> a = new ArrayList<String>(); System.out.println(a.getClass()); System.out.println(a.getClass().getGenericSuperclass()); ParameterizedTypeReference pt = new ParameterizedTypeReference<ArrayList<String>>() {}; System.out.println(pt.getType());
得到的結果是:
class java.util.ArrayList java.util.AbstractList<E> java.util.ArrayList<java.lang.String>
(4)excute
@Override public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = <span style="white-space:pre"> </span>new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables); }
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor<T> responseExtractor) throws RestClientException {…}
Callback interface for code that operates on a ClientHttpRequest. Allows to manipulate the request headers, and write to the request body.
簡單說:用於操作請求頭和body,在請求發出前執行。
該接口有兩個實現類:
AcceptHeaderRequestCallback | 只處理請求頭,用於getXXX()方法。 |
HttpEntityRequestCallback | 繼承於AcceptHeaderRequestCallback可以處理請求頭和body,用於putXXX()、postXXX()和exchange()方法。 |
* DELETE、HEAD、OPTIONS沒有使用這個接口。
簡單說:解析HTTP響應的數據,而且不需要擔心異常和資源的關閉。
HeadersExtractor | 用於提取請求頭。 |
HttpMessageConverterExtractor | 用於提取響應body。 |
ResponseEntityResponseExtractor | 使用HttpMessageConverterExtractor提取body(委托模式),然后將body和響應頭、狀態封裝成ResponseEntity對象。 |
(1)提取器HttpMessageConverterExtractor尋找可用的轉化器
轉化器 |
可轉化的類型 |
ByteArrayHttpMessageConverter | byte[] |
StringHttpMessageConverter | String |
ResourceHttpMessageConverter | Resource |
SourceHttpMessageConverter | javax.xml.transform.* |
AllEncompassingFormHttpMessageConverter | MultiValueMap |
Jaxb2RootElementHttpMessageConverter | XmlRootElement,XmlType(注解) |
... | |
MappingJackson2HttpMessageConverter | Json |
提取器遍歷轉化器集合以查找可用的轉化器,其中MappingJackson2HttpMessageConverter總是在最后一個,因為該類實現了GenericHttpMessageConverter,算是一個通用轉化器,只有在找不到合適的轉化器時才輪到它。Spring提供了一個該類的實現,以保證總是能得到該類。
(2)轉化器尋找可用的反序列化器
如果已有可用的反序列化器,則直接返回。否則創建一個新的反序列化器。
BeanDeserializerFactory.buildBeanDeserializer
BeanDeserializerFactory.createBeanDeserializer
(3)反序列化器執行反序列化
TOKEN |
Json的一個或一組字符 |
START_OBJECT | { |
END_OBJECT | } |
START_ARRAY | [ |
END_ARRAY | ] |
VALUE_TRUE | true |
VALUE_FALSE | false |
... |
調用棧: