使用 Spring RestTemplate 访问 Rest 服务

RestTemplate简介

Spring's central class for synchronous client-side HTTP access.
It simplifies communication with HTTP servers, and enforces RESTful principles.
It handles HTTP connections, leaving application code to provide URLs(with possible template variables) and extract results.

  上面这段是RestTemplate类中的简单介绍,RestTemplate是Spring3.0后开始提供的用于访问 Rest 服务的轻量级客户端,相较于传统的HttpURLConnection、Apache HttpClient、OkHttp等框架,RestTemplate大大简化了发起HTTP请求以及处理响应的过程。本文关注RestTemplate是如何使用的,暂不涉及内部的实现原理。

  RestTemplate支持多种的请求方式,具体参考下表:

HTTP method RestTemplate methods
GET getForObject、getForEntity
POST postForObject、postForEntity、postForLocation
PUT put
DELETE delete
HEAD headForHeaders
OPTIONS optionsForAllow
PATCH patchForObject
any exchange、execute

引入RestTemplate

  • 方式一,使用无参构造器直接new一个对象
    private RestTemplate restTemplate = new RestTemplate();
  • 方式二,先注册成Spring的Bean对象,之后使用的时候直接注入
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    @Autowired
    private RestTemplate restTemplate;

测试准备

  新建User对象,用于下面不同请求方式的测试。

@Data
public class User {

    /**
     * id
     */
    private Long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 年龄
     */
    private Integer age;
    
}

GET请求

  GET请求对应两个方法,getForObject()和getForEntity(),每个方法又对应有具体的三个重载方法。这两者的区别在于getForObject()返回的是一个简单的对象,而getForEntity()响应的数据中,还额外包含有与HTTP相关的信息,如响应码、响应头等。

    /**
     * GET资源 (发送一个HTTP GET请求,返回的请求体将映射为一个对象)
     * <p>
     * 1. 执行根据URL检索资源的GET请求
     * 2. 根据responseType参数匹配为一定的类型
     * 3. getForObject()只返回所请求类型的对象信息
     */
    @Test
    public void getForObject() {
        long id = 0;
        //URL中的{id}占位符最终将会用方法的id参数来填充
        String url = "http://localhost:9000/user/{id}";


        //重载1:最后一个参数是大小可变的参数列表,每个参数都会按出现顺序插入到指定URL的占位符中
        User user = restTemplate.getForObject(url, User.class, id);
        System.out.println("user = " + user);

        //重载2:将id参数放到Map中,并以id作为key,然后将这个Map作为最后一个参数
        Map<String, String> urlParams = new HashMap<>(1);
        urlParams.put("id", String.valueOf(id));
        User user2 = restTemplate.getForObject(url, User.class, urlParams);
        System.out.println("user2 = " + user2);

        //重载3:构造URL对象,要在url上进行字符串拼接,不推荐使用
        url = "http://localhost:9000/user/" + id;
        User user3 = restTemplate.getForObject(URI.create(url), User.class);
        System.out.println("user3 = " + user3);
    }
   /**
     * GET资源 (发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象)
     * <p>
     * 1. 执行根据URL检索资源的GET请求
     * 2. 根据responseType参数匹配为一定的类型
     * 3. getForEntity()方法会返回请求的对象以及响应相关的额外信息
     */
    @Test
    public void getForEntity() {
        long id = 1;
        //URL中的{id}占位符最终将会用方法的id参数来填充
        String url = "http://localhost:9000/user/{id}";

        //重载1:同getForObject(),只不过返回的类型是ResponseEntity
        ResponseEntity<User> userResponseEntity = restTemplate.getForEntity(url, User.class, id);
        User user = userResponseEntity.getBody();
        HttpStatus statusCode = userResponseEntity.getStatusCode();
        int statusCodeValue = userResponseEntity.getStatusCodeValue();
        HttpHeaders headers = userResponseEntity.getHeaders();
        System.out.println("user = " + user + "; statusCode = " + statusCode + "; statusCodeValue = " + statusCodeValue + "; headers = " + headers);

        //重载1:同getForObject(),只不过返回的类型是ResponseEntity
        Map<String, String> urlParams = new HashMap<>(1);
        urlParams.put("id", String.valueOf(id));
        ResponseEntity<User> userResponseEntity2 = restTemplate.getForEntity(url, User.class, urlParams);
        System.out.println("userResponseEntity2 = " + userResponseEntity2);

        //重载3:同getForObject(),只不过返回的类型是ResponseEntity
        url = "http://localhost:9000/user/" + id;
        ResponseEntity<User> userResponseEntity3 = restTemplate.getForEntity(URI.create(url), User.class);
        System.out.println("userResponseEntity3 = " + userResponseEntity3);
    }

POST请求

  POST请求对应三个方法,postForObject()、postForEntity()和postForLocation(),每个方法同样对应有三个具体的重载方法。postForObject()、postForEntity()类似于getForObject()和postForEntity(),postForLocation()返回的是一个URI对象。

    /**
     * POST资源 (POST数据到一个URL,返回根据响应体匹配形成的对象)
     */
    @Test
    public void postForObject() {
        String url = "http://localhost:9000/user";

        //重载1 & 重载2
        User user1 = new User();
        user1.setAge(20);
        user1.setUsername("张三");
        //第4个参数可以是Object... uriVariables 或者 Map<String, ?> uriVariables
        User u1 = restTemplate.postForObject(url, user1, User.class);
        System.out.println("user1 = " + u1);

        //重载3
        User user2 = new User();
        user2.setAge(30);
        user2.setUsername("李四");
        User u2 = restTemplate.postForObject(URI.create(url), user2, User.class);
        System.out.println("user2 = " + u2);
    }
    /**
     * POST资源 (POST数据到一个URL,返回包含一个对象的ResponseEntity,这个对象是从响应体中映射得到的)
     */
    @Test
    public void postForEntity() {
        String url = "http://localhost:9000/user";

        // 重载1 & 重载2
        User user3 = new User();
        user3.setAge(25);
        user3.setUsername("王五");
        // 第4个参数可以是Object... uriVariables 或者 Map<String, ?> uriVariables
        ResponseEntity<User> userResponseEntity = restTemplate.postForEntity(url, user3, User.class);
        User userBody = userResponseEntity.getBody();
        HttpStatus statusCode = userResponseEntity.getStatusCode();
        int statusCodeValue = userResponseEntity.getStatusCodeValue();
        HttpHeaders headers = userResponseEntity.getHeaders();
        System.out.println("user = " + userBody + "; statusCode = " + statusCode + "; statusCodeValue = " + statusCodeValue + "; headers = " + headers);


        // 重载3
        User user4 = new User();
        user4.setAge(35);
        user4.setUsername("陆六");
        ResponseEntity<User> userResponseEntity2 = restTemplate.postForEntity(URI.create(url), user4, User.class);
        System.out.println("userResponseEntity2 = " + userResponseEntity2);
    }
    /**
     * POST资源 (POST数据到一个URL)
     * 如果服务端在响应的Location头信息中返回新资源的URL,接下来postForLocation()会以String的格式返回该URL
     */
    @Test
    public void postForLocation() {
        String url = "http://localhost:9000/user";
        User user = new User();
        user.setAge(28);
        user.setUsername("七七");

        // 重载1 & 重载2
        // 第3个参数可以是Object... uriVariables 或者 Map<String, ?> uriVariables
        URI uri = restTemplate.postForLocation(url, user);
        if (Objects.nonNull(uri)) {
            String location = uri.toString();
            System.out.println("location = " + location);
        }


        // 重载3
        URI uri1 = restTemplate.postForLocation(URI.create(url), user);
        if (Objects.nonNull(uri1)) {
            String location = uri1.toString();
            System.out.println("location = " + location);
        }
    }

PUT请求

  PUT请求只有一个方法:put(),对应三个具体的重载方法,put请求返回值为void

    /**
     * PUT资源 (PUT资源到特定的URL)
     */
    @Test
    public void put() {
        long id = 1;
        //URL中的{id}占位符最终将会用方法的id参数来填充
        String url = "http://localhost:9000/user/{id}";

        User user = new User();
        user.setId(id);
        user.setUsername("update 张三");
        user.setAge(99);

        //重载1
        restTemplate.put(url, user, id);

        //重载2
        Map<String, String> urlParams = new HashMap<>(1);
        urlParams.put("id", String.valueOf(id));
        restTemplate.put(url, user, urlParams);

        //重载3
        restTemplate.put(URI.create("http://localhost:9000/user/" + id), user);
    }

DELETE请求

  DELETE请求同样只有一个方法:delete(),对应有三个具体的重载方法,delete请求返回值为void

    /**
     * DELETE资源 (在特定的URL上对资源执行HTTP DELETE操作)
     */
    @Test
    public void delete() {
        long id = 1;
        //URL中的{id}占位符最终将会用方法的id参数来填充
        String url = "http://localhost:9000/user/{id}";

        //重载1
        restTemplate.delete(url, id);

        //重载2
        Map<String, String> urlParams = new HashMap<>(1);
        urlParams.put("id", String.valueOf(id));
        restTemplate.delete(url, urlParams);

        //重载3
        restTemplate.delete(URI.create("http://localhost:9000/user/" + id));
    }

HEAD & OPTIONS & PATCH 请求

  这几种请求方式比较少见和少用,这里就不再说明了。

any(通用)请求

  通用的请求主要是指execute()和exchange()方法,这两个方法又分别对应有三个和八个具体的重载方法。

    /**
     * 交换资源 (在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity,这个对象是从响应体中映射得到的)
     * 允许在发送给服务端的请求中设置头信息
     * 支持GET、POST、PUT、DELETE...
     */
    @Test
    public void exchange() {
        long id = 1;
        String url = "http://localhost:9000/user/{id}";

        //GET资源
        //参数3是请求头部分;参数4是响应数据要转成对象;最后一个参数用于替换URL中的占位符
        ResponseEntity<User> userResponseEntity = restTemplate.exchange(url, HttpMethod.GET, null, User.class, id);
        System.out.println("exchange = " + userResponseEntity + "; response body = " + userResponseEntity.getBody());

        //POST资源
        String url2 = "http://localhost:9000/user";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        String jsonParams = "{\"username\":\"123\",\"age\":23}";
        HttpEntity<User> httpEntity = new HttpEntity(jsonParams, headers);
        ResponseEntity<User> responseEntity = restTemplate.exchange(url2, HttpMethod.POST, httpEntity, User.class);
        System.out.println("exchange = " + responseEntity + "; response body = " + responseEntity.getBody());

        //PUT and DELETE忽请自行测试
    }

  execute()的操作相对而言会比较麻烦,建议大家多使用exchange(),这里就不再贴代码进行说明了。

补充说明

  以上测试代码可以在我的GitHub仓库中找到。

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