Spring Boot使用Spring RestTemplate详解

1、什么是REST?

REST(Representational State Transfer)是Roy Fielding 提出的一个描述互联系统架构风格的名词。REST定义了一组体系架构原则,您可以根据这些原则设计以系统资源为中心的Web 服务,包括使用不同语言编写的客户端如何通过 HTTP处理和传输资源状态。

为什么称为 REST?Web本质上由各种各样的资源组成,资源由URI 唯一标识。浏览器(或者任何其它类似于浏览器的应用程序)将展示出该资源的一种表现方式,或者一种表现状态。如果用户在该页面中定向到指向其它资源的链接,则将访问该资源,并表现出它的状态。这意味着客户端应用程序随着每个资源表现状态的不同而发生状态转移,也即所谓REST。

附:REST定义RESTSOAP的比较

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没有使用这个接口。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式。”但是在要求详细讲述它所提出的各个约束,以及如...
    时待吾阅读 3,417评论 0 19
  • API定义规范 本规范设计基于如下使用场景: 请求频率不是非常高:如果产品的使用周期内请求频率非常高,建议使用双通...
    有涯逐无涯阅读 2,521评论 0 6
  • 16. Web MVC 框架 16.1 Spring Web MVC 框架介绍 Spring Web 模型-视图-...
    此鱼不得水阅读 1,026评论 0 4
  • 组织:中国互动出版网(http://www.china-pub.com/) RFC文档中文翻译计划(http://...
    Palomar阅读 1,570评论 0 6
  • 敬爱的李老师,智慧的教授,亲爱的家人们: 大家好!我是来自长兴南方眼视光中心的杨军微。今天是2019年3月...
    杨军微阅读 179评论 0 1