1、什么是REST?
REST(Representational State 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方法之间的一对一映射。根据此映射:
若要在服务器上创建资源,应该使用POST 方法。
若要检索某个资源,应该使用GET 方法。
若要更改资源状态或对其进行更新,应该使用PUT 方法。
若要删除某个资源,应该使用DELETE 方法。
3、HTTP请求的方法
- GET:通过请求URI得到资源
- POST:用于添加新的内容
- PUT:用于修改某个内容,若不存在则添加
- DELETE:删除某个内容
- OPTIONS :询问可以执行哪些方法
- HEAD :类似于GET, 但是不返回body信息,用于检查对象是否存在,以及得到对象的元数据
- CONNECT :用于代理进行传输,如使用SSL
- TRACE:用于远程诊断服务器
4、HTTP请求的状态码
- 成功Successful 2xx:此类状态码标识客户端的请求被成功接收、理解并接受。常见如200(OK)、204(NoContent)。
- 重定向Redirection 3xx:这个类别的状态码标识用户代理要做出进一步的动作来完成请求。常见如301(MovedPermanently)、302(MovedTemprarily)。
- 客户端错误Client Error 4xx:4xx类别的状态码是当客户端出错的时候使用的。常见如400(BadRequest)、401(Unauthorized)、403(Forbidden)、404(NotFound)。
- 服务器错误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's central class for synchronous client-side HTTP access. It simplifies communication with HTTPservers, and enforces RESTful principles. It handles HTTP connections, leaving application code to provide URLs(with possible template variables) and extract results.
简单说就是:简化了发起HTTP请求以及处理响应的过程,并且支持REST。为什么说简化了呢?
来看两种实现方式
(1)使用java.net包下的URLConnection建立连接
String result= "";
BufferedReader in = null;
try {
String urlNameString= url +"?" + param;
URL realUrl= new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = 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(new InputStreamReader(
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。但它们的最后一个参数分别是Object[]和Map
第三种的首个参数使用java.net.URI表示一个URI。且只有两个参数。这是因为,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>
- 这是因为
ParameterizedTypeReference<ArrayList<String>>
并不根据实参而是使用getGenericSuperclass()
方法获取其父类的类型(注意这里的new有花括号,是ParameterizedTypeReference的子类),父类的类型通过java.lang.reflect.Type描述,然后通过Type的getActualTypeArguments()获得了父类的实参类型,注意得到的Type类,并不是class类。
(4)excute
所有的get、post、delete、put、options、head、exchange方法最终调用的都是excute方法。举个栗子:
@Override
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
Excute方法只是将String格式的URI转成了java.net.URI,之后调用了doExecute方法。整个调用过程
(5)doExcute
5.1 定义
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor<T> responseExtractor) throws RestClientException {…}
这里需要了解两个类: RequestCallback &ResponseExtractor
5.2 RequestCallback
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没有使用这个接口。