【Spring】RestTemplate基础用法小结

在这里插入图片描述

1.概述

转载:RestTemplate基础用法小结

在Spring项目中,通常会借助RestTemplate来实现网络请求,RestTemplate封装得很完善了,基本上可以非常简单的完成各种HTTP请求,本文主要介绍一下基本操作,最常见的GET/POST请求的使用姿势

2.项目搭建

2.1. 配置

借助SpringBoot搭建一个SpringWEB项目,提供一些用于测试的REST服务

SpringBoot版本: 2.2.1.RELEASE
核心依赖: spring-boot-stater-web
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

为了后续输出的日志更直观,这里设置了一下日志输出格式,在配置文件application.yml中,添加

logging:
  pattern:
    console: (%msg%n%n){blue}

2.2. Rest服务

添加三个接口,分别提供GET请求,POST表单,POST json对象,然后返回请求头、请求参数、cookie,具体实现逻辑相对简单,也不属于本篇重点,因此不赘述说明

package com.spring.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import sun.misc.BASE64Decoder;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;

@RestController
public class RestTemplateController {

    private String getHeaders(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        String name;

        JSONObject headers = new JSONObject();
        while (headerNames.hasMoreElements()) {
            name = headerNames.nextElement();
            headers.put(name, request.getHeader(name));
        }
        return headers.toJSONString();
    }

    private String getParams(HttpServletRequest request) {
        return JSONObject.toJSONString(request.getParameterMap());
    }

    private String getCookies(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null || cookies.length == 0) {
            return "";
        }

        JSONObject ck = new JSONObject();
        for (Cookie cookie : cookies) {
            ck.put(cookie.getName(), cookie.getValue());
        }
        return ck.toJSONString();
    }

    private String buildResult(HttpServletRequest request) {
        return buildResult(request, null);
    }

    private String buildResult(HttpServletRequest request, Object obj) {
        String params = getParams(request);
        String headers = getHeaders(request);
        String cookies = getCookies(request);

        if (obj != null) {
            params += " | " + obj;
        }

        return "params: " + params + "\nheaders: " + headers + "\ncookies: " + cookies;
    }

    @GetMapping(path = "get")
    public String get(HttpServletRequest request) {
        return buildResult(request);
    }

    @PostMapping(path = "post")
    public String post(HttpServletRequest request) {
        return buildResult(request);
    }

    @Data
    @NoArgsConstructor
    public static class ReqBody implements Serializable {
        private static final long serialVersionUID = -4536744669004135021L;
        private String name;
        private Integer age;
    }

    @PostMapping(path = "body")
    public String postBody(@RequestBody ReqBody body) {
        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return buildResult(request, body);
    }

    @GetMapping(path = "timeout")
    public String timeOut(HttpServletRequest request) throws InterruptedException {
        Thread.sleep(60_000L);
        return buildResult(request);
    }


    /**
     * 标准的http auth验证
     *
     * @param request
     * @param response
     * @return
     */
    @GetMapping(path = "auth")
    public String auth(HttpServletRequest request, HttpServletResponse response) throws IOException, IOException {
        String auth = request.getHeader("Authorization");
        if (StringUtils.isEmpty(auth)) {
            response.setStatus(401);
            response.setHeader("WWW-Authenticate", "Basic realm=\"input username and password\"");
            return buildResult(request) + "\n>>>no auth header";
        }

        String[] userAndPass = new String(new BASE64Decoder().decodeBuffer(auth.split(" ")[1])).split(":");
        if (userAndPass.length < 2) {
            response.setStatus(401);
            response.setHeader("WWW-Authenticate", "Basic realm=\"input username and password\"");
            return buildResult(request) + "\n>>>illegal auth: " + auth;
        }

        if ("user".equalsIgnoreCase(userAndPass[0]) && "pwd".equalsIgnoreCase(userAndPass[1])) {
            return buildResult(request) + "\n>>>auth: success!";
        }

        response.setStatus(401);
        response.setHeader("WWW-Authenticate", "Basic realm=\"input username and password\"");
        return buildResult(request) + "\n>>>illegal user or pwd!";
    }

    @GetMapping(path = "atimeout")
    public String aTimeOut(HttpServletRequest request) throws InterruptedException {
        Thread.sleep(3_000L);
        return "time out!" + JSON.toJSONString(request.getParameterMap());
    }

    @GetMapping(path = "4xx")
    public String _4xx(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(401);
        return "return 401 : " + JSON.toJSONString(request.getParameterMap());
    }
}

3.RestTemplate示例

3.1. Get请求

使用RestTemplate发起GET请求,通常有两种常见的方式

  1. getForEntity: 返回的正文对象包装在HttpEntity实体中,适用于获取除了返回的正文之外,对返回头、状态码有需求的场景

  2. getForObject: 返回正文,适用于只对正文感兴趣的场景
    上面这两种方法除了返回结果不同之外,其他的使用姿势基本一样,有三种

@Nullable
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;

@Nullable
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

@Nullable
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException;

上面三个重载方法,区别仅在于GET参数如何处理,下面给出一个实例进行说明

/**
     * 基本的get请求
     */
    @Test
    public void basicGet() {
        RestTemplate restTemplate = new RestTemplate();

        HttpEntity<String> res =
                restTemplate.getForEntity("http://127.0.0.1:8082/get?name=一灰灰Blog&age=20", String.class);
        log.info("Simple getForEntity res: {}", res);

        // 直接在url中拼参数,调用的是前面对应的方法1; 当然这里也可以将url转成URI格式,这样调用的是方法3
        String response = restTemplate.getForObject("http://127.0.0.1:8082/get?name=一灰灰Blog&age=20", String.class);
        log.info("Simple getForObject res: {}", response);

        // 通过可变参数,填充url参数中的{?}, 注意顺序是对应的
        response = restTemplate.getForObject("http://127.0.0.1:8082/get?name={?}&age={?}", String.class, "一灰灰Blog", 20);
        log.info("Simple getForObject by uri params: {}", response);


        // 参数放在map中,通过map的key与url中的{}进行匹配,实现参数替换
        response = restTemplate.getForObject("http://127.0.0.1:8082/get?name={name}&age={age}", String.class,
                new HashMap<String, Object>() {
                    {
                        put("name", "一灰灰Blog");
                        put("age", 20);
                    }
                });
        log.info("Simple getForObject by uri map params: {}", response);
    }


输出结果如下:

(Simple getForEntity res: <200,params: {"name":["一灰灰Blog"],"age":["20"]}
headers: {"host":"127.0.0.1:8080","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies: ,[Content-Type:"text/plain;charset=UTF-8", Content-Length:"213", Date:"Wed, 17 Jun 2020 01:14:16 GMT"]>

(Simple getForObject res: params: {"name":["一灰灰Blog"],"age":["20"]}
headers: {"host":"127.0.0.1:8080","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies: 

(Simple getForObject by uri params: params: {"name":["一灰灰Blog"],"age":["20"]}
headers: {"host":"127.0.0.1:8080","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies: 

(Simple getForObject by uri map params: params: {"name":["一灰灰Blog"],"age":["20"]}
headers: {"host":"127.0.0.1:8080","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies:

3.2. POST表单

POST表单属于非常基础的请求方式了,根据返回结果,RestTemplate同样提供了两种姿势

postForEntity: 返回的正文封装在HttpEntity
postForObject: 直接返回正文对象

它的使用姿势一样有三种

@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);

@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);

@Nullable
public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType);

请注意上面三个方法的前两个,后面的uriVariables是url参数,不是POST表单参数哦,它们之间是有区别的(虽然我们一般post提交表单时,不怎么会在url中添加参数)

下面还是根据实际的用例来查看

 /**
     * post基本请求
     *
     * 先启动主类,然后运行测试类
     */
    @Test
    public void basicPost() {
        RestTemplate restTemplate = new RestTemplate();

        HttpEntity<String> res = restTemplate
                .postForEntity("http://127.0.0.1:8080/post?name=一灰灰Blog&age=20", new LinkedMultiValueMap<>(),
                        String.class);
        log.info("Simple postForEntity res: {}", res);

        // 提交的表单参数
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("name", "一灰灰Blog");
        params.add("age", 20);

        String response = restTemplate.postForObject("http://127.0.0.1:8080/post", params, String.class);
        log.info("Simple postForObject res: {}", response);
        // 这个url参数是放在url上面的哦
        response = restTemplate.postForObject("http://127.0.0.1:8080/post?urlName={?}", params, String.class, "url参数");
        log.info("Simple postForObject with urlParams res: {}", response);

        response = restTemplate.postForObject("http://127.0.0.1:8080/post?urlName={urlName}", params, String.class,
                new HashMap<String, Object>() {{
                    put("urlName", "url参数");
                }});
        log.info("Simple postForObject with map urlParams res: {}", response);
    }

测试输出如下

(Simple postForEntity res: <200,params: {"name":["一灰灰Blog"],"age":["20"]}
headers: {"content-length":"0","host":"127.0.0.1:8080","content-type":"application/x-www-form-urlencoded;charset=UTF-8","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies: ,[Content-Type:"text/plain;charset=UTF-8", Content-Length:"299", Date:"Wed, 17 Jun 2020 01:14:16 GMT"]>

(Simple postForObject res: params: {"name":["一灰灰Blog"],"age":["20"]}
headers: {"content-length":"338","host":"127.0.0.1:8080","content-type":"multipart/form-data;charset=UTF-8;boundary=URAN0wQz_s1vauFbDLFRZ40bb3NtRRwgLuII-wCk","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies: 

(Simple postForObject with urlParams res: params: {"name":["一灰灰Blog"],"age":["20"],"urlName":["url参数"]}
headers: {"content-length":"314","host":"127.0.0.1:8080","content-type":"multipart/form-data;charset=UTF-8;boundary=2h15swz9SJPfjCxv2cNOwDn_TR2nK4gF","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies: 

(Simple postForObject with map urlParams res: params: {"name":["一灰灰Blog"],"age":["20"],"urlName":["url参数"]}
headers: {"content-length":"329","host":"127.0.0.1:8080","content-type":"multipart/form-data;charset=UTF-8;boundary=QOhroKp7BE4cNF5Oi3CJLdq_ixzk0t5ZZw9ch","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies:

3.3. post body

post一个json串,也属于比较常见的一种case了,对于RestTemplate而言,要支持这种方式,需要额外处理一下请求头,设置Content-Type为application/json

使用姿势和上面相差不大,只是需要注意一下请求参数的构建

 /**
     * json表单
     */
    @Test
    public void jsonPost() {
        RestTemplate restTemplate = new RestTemplate();

        // 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        // 提交的json串
        JSONObject params = new JSONObject();
        params.put("name", "一灰灰Blog");
        params.put("age", 20);

        // 构建请求参数
        HttpEntity<String> request = new HttpEntity<>(params.toJSONString(), headers);

        String response = restTemplate.postForObject("http://127.0.0.1:8080/body", request, String.class);
        log.info("json post res: {}", response);
    }

输出如下

(json post res: params: {} | DemoRest.ReqBody(name=???Blog, age=20)
headers: {"content-length":"27","host":"127.0.0.1:8080","content-type":"application/json","connection":"keep-alive","accept":"text/plain, application/json, application/*+json, */*","user-agent":"Java/1.8.0_171"}
cookies:

请注意,提交的json串中文乱码了

4. 小结

上面主要介绍的是RestTemplate的常规用法,基础的GET/POST请求姿势,如果业务简单,不需要考虑各种复杂的异常情况,也是没有太多的问题了;那么如果需要考虑,又有哪些需要注意的呢?

上面的中文乱码问题如何解决?
自定义的请求头如何塞入(如果是爬虫,上面的User-Agent太容易被屏蔽了)
cookie设置
REST支持Basic Auth的验证方式如何发起请求
超时设置
自定义连接池替换
REST返回非200状态码的情况,能否不抛异常,自定义处理?
ssl校验
…
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值