Http请求-RestTemplate

在这里插入图片描述

0. 概述

RestTemplate是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。

  • getForObject:Retrieves a representation via GET.
  • getForEntity:Retrieves a ResponseEntity (that is, status, headers, and body) by using GET.
  • headForHeaders:Retrieves all headers for a resource by using HEAD.
  • postForLocation:Creates a new resource by using POST and returns the Location header from the response.
  • postForObject:Creates a new resource by using POST and returns the representation from the response.
  • postForEntity:Creates a new resource by using POST and returns the representation from the response.
  • put:Creates or updates a resource by using PUT.
  • delete:Deletes the resources at the specified URI by using DELETE.
  • exchange:More generalized (and less opinionated) version of the preceding methods that provides extra flexibility when needed. It accepts a RequestEntity (including HTTP method, URL, headers, and body as input) and returns a ResponseEntity.
  • execute:The most generalized way to perform a request, with full control over request preparation and response extraction through callback interfaces.

getForObject - delete:提供常规的 Rest API(GET、POST、DELETE 等)方法调用;

exchange:接收一个 RequestEntity 参数,可以自己设置 HTTP method,URL,headers 和 body,返回 ResponseEntity;

execute:通过 callback 接口,可以对请求和返回做更加全面的自定义控制。

一般情况下,我们使用第一组和第二组方法就够了。


1. RestTemplate配置

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

RestTemplate配置初始化为一个Bean

@Configuration
public class RestTemplateConfig {
    /**
     * 没有实例化RestTemplate时,初始化RestTemplate
     */
    @ConditionalOnMissingBean(RestTemplate.class)
    @Bean
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }
}

默认使用了 JDK 自带的HttpURLConnection作为底层 HTTP 客户端实现,可以使用ClientHttpRequestFactory指定不同的 HTTP 连接方式。比如,将其改成HttpClient客户端:

@Configuration
public class RestTemplateConfig {

   @ConditionalOnMissingBean(RestTemplate.class)
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory){
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
        return restTemplate;
    }

    /**
     * 使用HttpClient作为底层客户端
     * @return
     */
    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        int timeout = 5000;
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(timeout)
                .setConnectionRequestTimeout(timeout)
                .setSocketTimeout(timeout)
                .build();
        CloseableHttpClient client = HttpClientBuilder
                .create()
                .setDefaultRequestConfig(config)
                .build();
        return new HttpComponentsClientHttpRequestFactory(client);
    }
}

在需要使用RestTemplate的位置,注入并使用即可!

@Autowired
private RestTemplate restTemplate;

从开发人员的反馈,和网上的各种 HTTP 客户端性能以及易用程度评测来看,OkHttp 优于 Apache的HttpClientApache的HttpClient优于HttpURLConnection

因此,我们还可以通过如下方式,将底层的 HTTP 客户端换成OkHttp

/**
 * 使用OkHttpClient作为底层客户端
 * @return
 */
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS)
            .build();
    return new OkHttp3ClientHttpRequestFactory(okHttpClient);
}

如果需要配置http连接管理器,完整配置如下:

@Configuration
public class RestTemplateConfig {
    
    /**
     * http连接管理器
     * @return
     */
    @Bean
    public HttpClientConnectionManager poolingHttpClientConnectionManager() {
        /*// 注册http和https请求
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);*/
        
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        // 最大连接数
        poolingHttpClientConnectionManager.setMaxTotal(500);
        // 同路由并发数(每个主机的并发)
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
        return poolingHttpClientConnectionManager;
    }
    
    /**
     * HttpClient
     * @param poolingHttpClientConnectionManager
     * @return
     */
    @Bean
    public HttpClient httpClient(HttpClientConnectionManager poolingHttpClientConnectionManager) {
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        // 设置http连接管理器
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
        
        /*// 设置重试次数
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));*/
        
        // 设置默认请求头
        /*List<Header> headers = new ArrayList<>();
        headers.add(new BasicHeader("Connection", "Keep-Alive"));
        httpClientBuilder.setDefaultHeaders(headers);*/
        
        return httpClientBuilder.build();
    }
    
    /**
     * 请求连接池配置
     * @param httpClient
     * @return
     */
    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        // httpClient创建器
        clientHttpRequestFactory.setHttpClient(httpClient);
        // 连接超时时间/毫秒(连接上服务器(握手成功)的时间,超出抛出connect timeout)
        clientHttpRequestFactory.setConnectTimeout(5 * 1000);
        // 数据读取超时时间(socketTimeout)/毫秒(务器返回数据(response)的时间,超过抛出read timeout)
        clientHttpRequestFactory.setReadTimeout(10 * 1000);
        // 连接池获取请求连接的超时时间,不宜过长,必须设置/毫秒(超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool)
        clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
        return clientHttpRequestFactory;
    }
    
    /**
     * rest模板
     * @return
     */
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
        // boot中可使用RestTemplateBuilder.build创建
        RestTemplate restTemplate = new RestTemplate();
        // 配置请求工厂
        restTemplate.setRequestFactory(clientHttpRequestFactory);
        return restTemplate;
    }
    
}

2. GET请求

在这里插入图片描述

  • getForObject():返回值是 HTTP 协议的响应体
  • getForEntity():返回的是ResponseEntityResponseEntity是对 HTTP 响应的封装,除了包含响应体,还包含HTTP状态码contentTypecontentLengthHeader等信息

2.1. getForObject

2.1.1. 带参的get请求(restful风格):

服务端:

@RestController
public class HelloController {

    @GetMapping("/testGet/{name}/{age}")
    public ResponseBean testGet(@PathVariable("name") String name, @PathVariable("age") int age){
        System.out.println("name="+name+",age="+age);
        ResponseBean response = new ResponseBean();
        response.setCode("200");
        response.setMsg("请求成功,方法:testGetByRestFul,请求参数id:" +  name + "、请求参数name:" + age);
        return response;
    }
}

响应类:

@Data
public class ResponseBean {
    private String code;
    private String msg;
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testRestGet1")
    public void  testRestGet1(){
        //请求地址
        String url = "http://localhost:8080/testGet/{1}/{2}";

        //发起请求,直接返回对象(restful风格)
        ResponseBean response = restTemplate.getForObject(url, ResponseBean.class, "lili", 23);
        System.out.println(response);
    }
}

访问地址:http://192.168.1.166:8080/testRestGet1

在这里插入图片描述

2.1.2. 带参的get请求(使用占位符号传参):

服务端:

@RestController
public class HelloController {

    @GetMapping("/testGet2")
    public ResponseBean testGet2(@RequestParam("name") String name, @RequestParam("age") int age){
        System.out.println("name="+name+",age="+age);
        ResponseBean response = new ResponseBean();
        response.setCode("200");
        response.setMsg("请求成功,方法:testGetByParam,请求参数id:" +  name + "、请求参数name:" + age);
        return response;
    }
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testRestGet2")
    public void  testRestGet2(){
        //请求地址
        String url = "http://localhost:8080/testGet2?name={name}&age={age}";

        //请求参数
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","lili");
        map.put("age",25);

        //发起请求,直接返回对象(带参数请求)
        ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, map);
        System.out.println(responseBean.toString());
    }
}

访问地址:http://192.168.1.166:8080/testRestGet2

在这里插入图片描述

使用name={name}&age={age}这种形式,最后一个参数是一个 map,map 的 key 即为前边占位符的名字,map 的 value 为参数值。

如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject

2.2. getForEntity

上面的所有的getForObject请求传参方法,getForEntity都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。

getForEntity方法的返回值是一个ResponseEntity<T>ResponseEntity<T>是 Spring 对 HTTP 请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。responseEntity.getBody()获取响应体。

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testRestGet2")
    public void  testRestGet2(){
        //请求地址
        String url = "http://localhost:8080/testGet2?name={name}&age={age}";

        //请求参数
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","lili");
        map.put("age",25);

        //发起请求
        ResponseEntity<ResponseBean> response = restTemplate.getForEntity(url, ResponseBean.class, map);

        // 获取响应体
        System.out.println("HTTP 响应body:[" + response.getBody().toString() + "]");

        // 以下是getForEntity比getForObject多出来的内容
        HttpStatus statusCode = response.getStatusCode();
        int statusCodeValue = response.getStatusCodeValue();
        HttpHeaders headers = response.getHeaders();

        System.out.println("HTTP 响应状态:[" + statusCode + "]");
        System.out.println("HTTP 响应状态码:[" + statusCodeValue + "]");
        System.out.println("HTTP Headers信息:[" + headers + "]");
    }
}

访问地址:http://192.168.1.166:8080/testRestGet2

在这里插入图片描述

注1:restTemplate 会根据 params 的具体类型,调用合适的 HttpMessageConvert 将请求参数写到请求体 body 中,并在请求头中添加合适的 content-type;

注2:也会根据 responseType 的类型(本例返回类类型,默认是 application/json),设置 head 中的 accept 字段,当响应返回的时候再调用合适的 HttpMessageConvert 进行响应转换。


3. POST请求

在这里插入图片描述

其实 POST 请求方法和 GET 请求方法上大同小异,RestTemplate 的 POST 请求也包含两个主要方法:

  • postForObject():返回body对象
  • postForEntity():返回全部的信息

3.1. postForObject

3.1.1. 表单请求:

服务端:

@RestController
public class HelloController {

    @PostMapping("testPost")
    public ResponseBean testPost(@RequestParam("name") String name,
                                       @RequestParam("age") String age){
        ResponseBean result = new ResponseBean();
        result.setCode("200");
        result.setMsg("请求成功,方法:testPostByForm,请求参数name:" + name + ",age:" + age);
        return result;
    }
}

请求类:

@Data
public class RequestBean {
    private String name;
    private String age;
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testRestPost1")
    public void  testRestPost1(){
        // 1. 请求地址
        String url = "http://localhost:8080/testPost";

        // 2.1 请求头设置,x-www-form-urlencoded格式的数据
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        // 2.2 提交参数设置
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("name", "唐三藏");
        map.add("age", "25");
        // 2.3组装请求体
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

        // 3. 发起请求
        ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
        System.out.println(responseBean.toString());
    }
}

访问地址:http://192.168.1.166:8080/testRestPost1

在这里插入图片描述
POST请求报文体需要使用MultiValueMap<String, String>,不能使用HashMap<String, Object>

3.1.2. 表单请求(传递对象):

服务端:

@RestController
public class HelloController {

    @PostMapping("testPost2")
    public ResponseBean testPost2(@RequestBody RequestBean request){
        ResponseBean result = new ResponseBean();
        result.setCode("200");
        result.setMsg("请求成功,方法:testPostByFormAndObj,请求参数:" + request);
        return result;
    }
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testRestPost2")
    public void  testRestPost2(){
        // 1. 请求地址
        String url = "http://localhost:8080/testPost2";

        // 2. 入参
        RequestBean request = new RequestBean();
        request.setName("唐三藏");
        request.setAge("23");

        // 3. 发起请求
        ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
        System.out.println(responseBean.toString());
    }
}

访问地址:http://192.168.1.166:8080/testRestPost2

在这里插入图片描述

3.2. postForEntity

上面的所有的postForObject请求传参方法,postForEntity都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testRestPost2")
    public void  testRestPost2(){
        // 1. 请求地址
        String url = "http://localhost:8080/testPost2";

        // 2. 入参
        RequestBean request = new RequestBean();
        request.setName("唐三藏");
        request.setAge("23");

        // 3. 发起请求
        ResponseEntity<ResponseBean> response = restTemplate.postForEntity(url, request, ResponseBean.class);
        System.out.println("HTTP 响应状态码:[" + response.getBody() + "]");
        System.out.println("HTTP 响应状态:[" + response.getStatusCode() + "]");
        System.out.println("HTTP 响应状态码:[" + response.getStatusCodeValue() + "]");
        System.out.println("HTTP Headers信息:[" + response.getHeaders() + "]");
    }
}

访问地址:http://192.168.1.166:8080/testRestPost2

在这里插入图片描述

3.3 postForLocation

postForLocation用于页面重定向,postForLocation 的参数和前面两种的参数基本一致,只不过该方法的返回值为 URI ,这个只需要服务提供者返回一个 URI 即可,该 URI 表示新资源的位置。

服务端:

@RestController
public class HelloController {

    @PostMapping("testPostByLocation")
    public String testPostByLocation(@RequestBody RequestBean request){
        return "redirect:index.html";
    }
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testRestLocation")
    public void  testRestLocation(){
        // 1. 请求地址
        String url = "http://localhost:8080/testPostByLocation";

        // 2. 入参
        RequestBean request = new RequestBean();
        request.setName("唐三藏");
        request.setAge("23");

        // 3. 用于提交完成数据之后的页面跳转,返回跳转url
        URI uri = restTemplate.postForLocation(url, request);
        System.out.println(uri.toString());
    }
}

4. PUT

PUT方法的参数和前面介绍的 postForEntity 方法的参数基本一致,只是 put 方法没有返回值而已。它指的是修改一个已经存在的资源或者插入资源,该方法会向URL代表的资源发送一个HTTP PUT方法请求。

在这里插入图片描述

服务端:

@RestController
public class HelloController {

    /**
     * 模拟JSON请求,put方法测试
     */
    @PutMapping("testPutByJson")
    public void testPutByJson(@RequestBody RequestBean request){
        System.out.println("请求成功,方法:testPutByJson,请求参数:" + request);
    }
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    /**
     * 模拟JSON提交,put请求
     */
    @RequestMapping("/testPutByJson")
    public void testPutByJson(){
        //请求地址
        String url = "http://localhost:8080/testPutByJson";
        //入参
        RequestBean request = new RequestBean();
        request.setName("唐三藏");
        request.setAge("23");

        //模拟JSON提交,put请求
        restTemplate.put(url, request);
    }
}

5. DELETE

delete方法协议,表示删除一个已经存在的资源,该方法会向URL代表的资源发送一个HTTP DELETE方法请求。

在这里插入图片描述

服务端:

@RestController
public class HelloController {

    /**
     * 模拟JSON请求,delete方法测试
     */
    @DeleteMapping("testDeleteByJson")
    public void testDeleteByJson(){
        System.out.println("请求成功,方法:testDeleteByJson");
    }
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testDeleteByJson")
    public void testDeleteByJson(){
        //请求地址
        String url = "http://localhost:8080/testDeleteByJson";

        //模拟JSON提交,delete请求
        restTemplate.delete(url);
    }
}

6. EXCHANGE

如果以上方法还不满足你的要求。在RestTemplate工具类里面,还有一个exchange通用协议请求方法,它可以发送GET、POST、DELETE、PUT、OPTIONS、PATCH等 HTTP 方法请求。

  1. 允许调用者指定 HTTP 请求的方法(GET,POST,PUT等)
  2. 可以在请求中增加 body 以及头信息,其内容通过参数RequestEntity<?> requestEntity描述
  3. exchange支持‘含参数的类型’(即泛型类)作为返回类型,该特性通过ParameterizedTypeReferenceresponseType描述

服务端:

@RestController
public class HelloController {

    @PostMapping("testExchange")
    public ResponseBean testExchange(@RequestBody RequestBean request){
        ResponseBean result = new ResponseBean();
        result.setCode("200");
        result.setMsg("请求成功,方法:testExchange,请求参数:" + request);
        return result;
    }
}

请求端:

@RestController
public class TestRestTemplateController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/testExchange")
    public void testExchange() {
        UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("http://localhost:8080").
                path("/testExchange").build(true);
        URI uri = uriComponents.toUri();

        RequestBean request = new RequestBean();
        request.setName("唐三藏");
        request.setAge("23");

        RequestEntity<RequestBean> requestEntity = RequestEntity.post(uri).
                header(HttpHeaders.COOKIE,"key1=value1").
                accept(MediaType.APPLICATION_JSON).
                contentType(MediaType.APPLICATION_JSON).
                body(request);

        ResponseEntity<ResponseBean> responseEntity = restTemplate.exchange(requestEntity, ResponseBean.class);

        // 响应结果
        ResponseBean response = responseEntity.getBody();
        System.out.println("返回报文=" + response);
    }
}

访问地址:http://192.168.1.166:8080/testExchange

在这里插入图片描述

public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType)中,RequestEntity<?> requestEntity包含method,URL,contentType、accept、请求报文体等请求配置,Class<T> responseType指定响应参数类型。


7. 文件上传

参考Spring之RestTemplate详解,懒得写了。


一些其他设置

1. 拦截器配置
RestTemplate 也可以设置拦截器做一些统一处理。这个功能感觉和 Spring MVC 的拦截器类似。配置也很简单:

class MyInterceptor implements ClientHttpRequestInterceptor{

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            logger.info("enter interceptor...");
            return execution.execute(request,body);
        }
    }
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    RestTemplate restTemplate = new RestTemplate(factory);
    MyInterceptor myInterceptor = new MyInterceptor();
    List<ClientHttpRequestInterceptor> list = new ArrayList<>();
    list.add(myInterceptor);
    restTemplate.setInterceptors(list);
    return restTemplate;
}

2. ErrorHandler 配置
3. HttpMessageConverter
配置 RestTemplate 也可以配置 HttpMessageConverter,配置的原理和 Spring MVC 中类似。

引用:
Spring之RestTemplate详解

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会叫的狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值