Подробное объяснение использования RestTemplate для доступа к службам REST в Spring MVC.

Spring

В веб-разработке на Java нам обычно нужно запрашивать услуги, предоставляемые другими системами, через GET и POST. в,HttpURLConnection, поставляемый с JDK,Apache HttpClientи т. д. может быть достигнуто. Конечно, у этих методов есть явный недостаток, то есть код очень громоздкий. RestTemplate, предоставленный Spring, инкапсулирует реализацию этих библиотек, что может сделать наши HTTP-запросы более краткими и интуитивно понятными.

В RestTemplate определены одиннадцать отдельных операций:

метод описывать
 delete() Выполните операцию HTTP DELETE для ресурса по определенному URL-адресу.
 exchange() Выполните определенный метод HTTP для URL-адреса, возвращенный ResponseEntity содержит объекты, сопоставленные в ответ на
 execute() Метод, выполняемый для определенного URL-адреса HTTP, возвращает тело объекта, полученное из карты ответов.
 getForEntity() Отправьте HTTP-запрос GET, и возвращенный ResponseEntity содержит объект, сопоставленный телом ответа.
 getForObject() Отправьте запрос HTTP GET и верните объект, сопоставленный в соответствии с телом ответа.
 postForEntity() POST-данные в URL-адрес, возвращенный ResponseEntity содержит объект, с которым сопоставляется тело ответа.
 postForLocation() POST-данные на URL-адрес, возвращая URL-адрес вновь созданного ресурса
 postForObject() POST-данные в URL-адрес и возвращают объект, сопоставленный с телом ответа.
 put() PUT ресурс на определенный URL
 headForHeaders() Отправьте запрос HTTP HEAD и верните заголовок HTTP, содержащий URL-адрес определенного ресурса.
 optionsForAllow() Отправьте запрос HTTP OPTIONS и верните информацию заголовка Allow для определенного URL-адреса.

Далее я представлю несколько часто используемых методов.

(1) Добавьте зависимости в проект:

<!-- Jackson对自动解析JSON和XML格式的支持 -->
<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
 
<!-- HttpClient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
 
<!-- Fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.46</version>
</dependency>

(2) Вставьте RestTemplate в проект:

При внедрении bean-компонентов RestTemplate вы можете указать библиотеку классов, используемую RestTemplate для инициирования HTTP-запросов через ClientHttpRequestFactory. В связи с этим интерфейс ClientHttpRequestFactory в основном предоставляет следующие два метода реализации:

i) Фабрика SimpleClientHttpRequestFactory:

То есть нижний уровень использует метод, предоставляемый пакетом java.net, для создания запроса на соединение Http. Пример кода выглядит следующим образом:

package cn.zifangsky.springbootdemo.config;
 
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
 
@Configuration
public class RestTemplateConfig {
 
    /**
     * 返回RestTemplate
     * @param factory
     * @return
     */
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory){
        //消息转换器,一般情况下可以省略,只需要添加相关依赖即可
//        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
//        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
//        messageConverters.add(new FormHttpMessageConverter());
//        messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
//        messageConverters.add(new MappingJackson2HttpMessageConverter());
        
        RestTemplate restTemplate = new RestTemplate(factory);
//        restTemplate.setMessageConverters(messageConverters);
        
        return restTemplate;
    }
    
    /**
     * ClientHttpRequestFactory接口的第一种实现方式,即:
     * SimpleClientHttpRequestFactory:底层使用java.net包提供的方式创建Http连接请求
     * @return
     */
    @Bean
    public SimpleClientHttpRequestFactory simpleClientHttpRequestFactory(){
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        
        requestFactory.setReadTimeout(5000);
        requestFactory.setConnectTimeout(5000);
        
        return requestFactory;
    }
 
}
 

ii) HttpComponentsClientHttpRequestFactory (рекомендуется):

То есть нижний уровень использует пул соединений Httpclient для создания запросов на соединение Http. Пример кода выглядит следующим образом:

package cn.zifangsky.springbootdemo.config;
 
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
 
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
 
@Configuration
public class RestTemplateConfig {
 
    /**
     * 返回RestTemplate
     * @param factory
     * @return
     */
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory){
        //消息转换器,Spring Boot环境可省略,只需要添加相关依赖即可
//        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
//        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
//        messageConverters.add(new FormHttpMessageConverter());
//        messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
//        messageConverters.add(new MappingJackson2HttpMessageConverter());
        
        RestTemplate restTemplate = new RestTemplate(factory);
//        restTemplate.setMessageConverters(messageConverters);
        
        return restTemplate;
    }
    
    /**
     * ClientHttpRequestFactory接口的另一种实现方式(推荐使用),即:
     * HttpComponentsClientHttpRequestFactory:底层使用Httpclient连接池的方式创建Http连接请求
     * @return
     */
    @Bean
    public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory(){
        //Httpclient连接池,长连接保持30秒
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        
        //设置总连接数
        connectionManager.setMaxTotal(1000);
        //设置同路由的并发数
        connectionManager.setDefaultMaxPerRoute(1000);
        
        //设置header
        List<Header> headers = new ArrayList<Header>();
        headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04"));
        headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate"));
        headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"));
        headers.add(new BasicHeader("Connection", "keep-alive"));
        
        //创建HttpClient
        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connectionManager)
                .setDefaultHeaders(headers)
                .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //设置重试次数
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //设置保持长连接
                .build();
        
        //创建HttpComponentsClientHttpRequestFactory实例
        HttpComponentsClientHttpRequestFactory requestFactory = 
                new HttpComponentsClientHttpRequestFactory(httpClient);
        
        //设置客户端和服务端建立连接的超时时间
        requestFactory.setConnectTimeout(5000);
        //设置客户端从服务端读取数据的超时时间
        requestFactory.setReadTimeout(5000);
        //设置从连接池获取连接的超时时间,不宜过长
        requestFactory.setConnectionRequestTimeout(200);
        //缓冲请求数据,默认为true。通过POST或者PUT大量发送数据时,建议将此更改为false,以免耗尽内存
        requestFactory.setBufferRequestBody(false);
        
        return requestFactory;
    }
    
}
 

(3) Используйте метод getForObject(), чтобы инициировать запрос GET:

Метод GetForObject () на самом деле является дополнительным пакетом метода GetForentity (), а два аналогичны. Единственное отличие состоит в том, что метод GetForObject () возвращает только объект запрошенного типа, а метод GetForentity () возвращает дополнительную информацию, такую ​​как запрошенный объект и ответ на заголовок, код состояния ответа.

Сигнатуры трех методов getForObject() следующие:

<T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
 
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
 
<T> T getForObject(URI url, Class<T> responseType) throws RestClientException;

Пример кода выглядит следующим образом:

package cn.zifangsky.SpringBootDemo.controller;
 
import java.util.HashMap;
import java.util.Map;
 
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;
 
import cn.zifangsky.springbootdemo.config.RestTemplateConfig;
import cn.zifangsky.springbootdemo.config.WebMvcConfig;
import cn.zifangsky.springbootdemo.model.DemoObj;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={WebMvcConfig.class,RestTemplateConfig.class})
@WebAppConfiguration("src/main/resources")
public class TestRestTemplate {
 
    @Autowired
    private RestTemplate restTemplate;
    
    /**
     * 测试最基本的Get请求
     */
    @Test
    public void testGetMethod1(){
        DemoObj obj = restTemplate.getForObject("http://127.0.0.1:9090/rest/testJson2?id={1}&name={2}"
                , DemoObj.class
                , 1,"Tom");
        
        System.out.println(obj);
    }
 
}

Примечание. Службы REST, запрашиваемые модульным тестом в этой статье, взяты из предыдущей статьи:www.zifangsky.cn/1215.html

Приведенный выше код устанавливает параметры запроса для использования цифровых заполнителей, а последний параметр метода getForObject() является параметром переменной длины, который используется для замены предыдущих заполнителей один за другим. Конечно, в дополнение к этому методу вы также можете использовать Map для установки параметров, таких как:

    /**
     * 测试Get请求另一种设置参数的方式
     */
    @Test
    public void testGetMethod2(){
        Map<String, String> uriVariables = new HashMap<String, String>();
        uriVariables.put("var_id", "1");
        uriVariables.put("var_name", "Tom");
        
        DemoObj obj = restTemplate.getForObject("http://127.0.0.1:9090/rest/testJson2?id={var_id}&name={var_name}"
                , DemoObj.class
                , uriVariables);
        
        System.out.println(obj);
    }

После запуска модульных тестов окончательный вывод выглядит следующим образом:

DemoObj [id=2, name=Tom Ret]

此外需要注意的是,由于上面代码只是简单的单元测试,因此请求URL就直接硬编码在代码中了。实际开发则需要将之配置到配置文件或者Zookeeper、Redis中。 Например:

package cn.zifangsky.springbootdemo.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
 
import cn.zifangsky.springbootdemo.model.DemoObj;
 
@RestController
@RequestMapping("/restTemplate")
public class RestTemplateController {
 
    @Value("${SERVER_URL}")
    private String SERVER_URL;
    
    @Autowired
    private RestTemplate restTemplate;
    
    @RequestMapping(path="/getDemoObj",produces={MediaType.APPLICATION_JSON_UTF8_VALUE})
    public DemoObj getDemoObj(){
        DemoObj obj = restTemplate.getForObject(SERVER_URL + "/rest/testXML?id={1}&name={2}"
                , DemoObj.class
                , 1,"Tom");
        
        return obj;
    }
    
}
 

(4) Используйте метод getForEntity(), чтобы инициировать запрос GET:

Сигнатуры трех методов getForEntity() следующие:

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
 
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
 
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;

Пример кода выглядит следующим образом:

    /**
     * 测试Get请求返回详细信息,包括:响应正文、响应状态码、响应Header等
     */
    @Test
    public void testGetMethod3(){
        ResponseEntity<DemoObj> responseEntity = restTemplate.getForEntity("http://127.0.0.1:9090/rest/testJson2?id={1}&name={2}"
                , DemoObj.class
                , 1,"Tom");
        
        DemoObj body = responseEntity.getBody();
        int statusCodeValue = responseEntity.getStatusCodeValue();
        HttpHeaders headers = responseEntity.getHeaders();
        
        System.out.println("responseEntity.getBody():" + body);
        System.out.println("responseEntity.getStatusCodeValue():" + statusCodeValue);
        System.out.println("responseEntity.getHeaders():" + headers);
    }

После запуска модульных тестов окончательный вывод выглядит следующим образом:

responseEntity.getBody(): DemoObj [id=2, name=Том Рет]
responseEntity.getStatusCodeValue(): 200
Rensententitiity.Getheaders (): {дата = [пт, 09 февраля 2018 06:22:28 GMT], CONTENT-TYPE = [Приложение / JSON; CHARSET = UTF-8], Transfer-encoding = [Chunked]}

(5) Используйте метод postForObject(), чтобы инициировать запрос POST:

В RestTemplate запросы POST аналогичны запросам GET и также могут быть запрошены с использованием следующих трех методов:

<T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException;
 
<T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
            throws RestClientException;
 
<T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException;

Пример кода выглядит следующим образом:

    /**
     * 测试最基本的Post请求
     */
    @Test
    public void testPostMethod1(){
        DemoObj request = new DemoObj(1l, "Tim");
        
        DemoObj obj = restTemplate.postForObject("http://127.0.0.1:9090/rest/testJson1"
                , request, DemoObj.class);
        
        System.out.println(obj);
    }

После запуска модульных тестов окончательный вывод выглядит следующим образом:

DemoObj [id=2, name=Tim Ret]

(6) Используйте метод PostForentitority () для инициирования запроса на почту:

Сигнатуры трех методов postForEntity() следующие:

<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException;
 
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
            throws RestClientException;
 
<T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException;

Пример кода выглядит следующим образом:

    /**
     * 测试Post请求返回详细信息,包括:响应正文、响应状态码、响应Header等
     */
    @Test
    public void testPostMethod2(){
        DemoObj request = new DemoObj(1l, "Tim");
        
        ResponseEntity<DemoObj> responseEntity = restTemplate.postForEntity("http://127.0.0.1:9090/rest/testJson1"
                , request, DemoObj.class);
        
        DemoObj body = responseEntity.getBody();
        int statusCodeValue = responseEntity.getStatusCodeValue();
        HttpHeaders headers = responseEntity.getHeaders();
        
        System.out.println("responseEntity.getBody():" + body);
        System.out.println("responseEntity.getStatusCodeValue():" + statusCodeValue);
        System.out.println("responseEntity.getHeaders():" + headers);
    }

После запуска модульных тестов окончательный вывод выглядит следующим образом:

responseEntity.getBody(): DemoObj [id=2, name=Тим Рет]
responseEntity.getStatusCodeValue(): 200
responseEntity.getHeaders(): {Date=[Fri, 09 Feb 2018 06:32:02 GMT], Content-Type=[application/json;charset=utf-8], Transfer-Encoding=[chunked]}

(7) Используйте метод exchange() для выполнения указанного HTTP-запроса:

Отличие метода exchange() от описанных выше методов getForObject(), getForEntity(), postForObject(), postForEntity() заключается в том, что он может указывать HTTP-тип запроса. Пример кода выглядит следующим образом:

    /**
     * 测试Exchange请求
     */
    @Test
    public void testExchange(){
        //设置header
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/x-zifangsky");
        
        //设置参数
        String requestBody = "1#Converter";
        HttpEntity<String> requestEntity = new HttpEntity<String>(requestBody,headers);
 
        ResponseEntity<String> responseEntity = restTemplate.exchange("http://127.0.0.1:9090/convert"
                , HttpMethod.POST, requestEntity, String.class);
        
        System.out.println("responseEntity.getBody():" + responseEntity.getBody());
        System.out.println("responseEntity.getHeaders():" + responseEntity.getHeaders());
    }

После запуска модульных тестов окончательный вывод выглядит следующим образом:

responseEntity.getBody(): {"id":2,"name":"Converter Ret"}
responseEntity.getHeaders(): {Date=[Fri, 09 Feb 2018 06:42:29 GMT], Content-Type=[application/x-zifangsky], Transfer-Encoding=[chunked]}

Ссылаться на: