😊 @ 作者: 一恍过去
🎉 @ 主题: RestTemplate通过泛型实现POST、PUT、DELETE、GET、集合请求以及文件上传(可批量文件、可带参数)的统一封装(可打印日志)
⏱️ @ 创作时间: 2022年05月08日
目录
- 前言
- 1、RestTemplate配置
- 2、请求体封装
- 3、GET请求
- 4、POST请求
- 5、PUT请求
- 6、DELETE请求
- 7、List集合响应请求
- 8、文件传递请求(可批量文件、可带参数)
前言
RestTemplate:是一个在 Spring 框架中用于进行 RESTful 风格的 HTTP 请求的类。它提供了方便的方法来发送 HTTP 请求并处理响应。
Spring 的RestTemplate类简化了与 RESTful Web 服务进行通信的过程。它封装了底层的 HTTP 连接、请求和响应处理逻辑,使得开发者可以更加专注于业务逻辑而不必过多关注底层的网络细节。
使用 RestTemplate,可以执行各种 HTTP 方法,如 GET、POST、PUT、DELETE 等,并且可以将请求参数和头部信息设置为所需的值。还可以使用 RestTemplate 支持的各种响应解析器来处理返回的数据,如将响应转换为对象、字节数组、字符串等。
1、RestTemplate配置
引入POM:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
RestTemplateLog(请求日志打印) :
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
public class RestTemplateLog implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
traceResponse(response);
return response;
}
private void traceRequest(HttpRequest request, byte[] body) {
String path = request.getURI().getPath();
String header = String.valueOf(request.getHeaders());
String param = new String(body, StandardCharsets.UTF_8);
log.info("RestTemplate请求信息:【请求URL:{},请求头:{},请求参数:{}】", path, header, param);
}
private void traceResponse(ClientHttpResponse response) throws IOException {
StringBuilder inputStringBuilder = new StringBuilder();
try (BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) {
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
inputStringBuilder.append('\n');
line = bufferedReader.readLine();
}
}
int status = response.getStatusCode().value();
String body = String.valueOf(inputStringBuilder).trim();
log.info("RestTemplate响应信息:【状态码:{},响应参数:{}】", status, body);
}
}
RestTemplateConfig(请求配置):
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.web.client.RestTemplateBuilder;
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.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
@Configuration
public class RestTemplateConfig {
@Resource
private RestTemplateLog restTemplateLog;
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
// 添加拦截器
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(restTemplateLog);
restTemplate.setInterceptors(interceptors);
restTemplate.setRequestFactory(clientHttpRequestFactory());
// 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> converter = iterator.next();
if (converter instanceof StringHttpMessageConverter) {
iterator.remove();
}
}
messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
@Bean
public HttpClientConnectionManager poolingConnectionManager() {
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
// 连接池最大连接数
poolingConnectionManager.setMaxTotal(10000);
// 每个主机的并发
poolingConnectionManager.setDefaultMaxPerRoute(10000);
return poolingConnectionManager;
}
@Bean
public HttpClientBuilder httpClientBuilder() {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
//设置HTTP连接管理器
httpClientBuilder.setConnectionManager(poolingConnectionManager());
return httpClientBuilder;
}
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setHttpClient(httpClientBuilder().build());
// 连接超时,毫秒
clientHttpRequestFactory.setConnectTimeout(6000);
// 读写超时,毫秒
clientHttpRequestFactory.setReadTimeout(6000);
return clientHttpRequestFactory;
}
}
2、请求体封装
通过RestTemplate封装统一的POST、PUT、DELETE、GET,适合返回任意类型的对象,分别为doHttp与doHttpList两个方法,一个方法接收单个对象类型,一个方法接收集合对象;
FileRequestServer:
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class FileRequestServer {
@Resource
private RestTemplate restTemplate;
/**
* 默认不带头,并且请求类型为json
*
* @param url
* @param method
* @param obj
* @param tClass 为需要返回的实体类型
* @return
*/
public <T> T doHttp(String url, HttpMethod method, Object obj, Class<T> tClass) {
return doHttp(url, MediaType.APPLICATION_JSON, null, method, obj, tClass);
}
/**
* 默认不带头
*
* @param url
* @param contentType
* @param method
* @param obj
* @param tClass 为需要返回的实体类型
* @return
*/
public <T> T doHttp(String url, MediaType contentType, HttpMethod method, Object obj, Class<T> tClass) {
return doHttp(url, contentType, null, method, obj, tClass);
}
/**
* 默认带头,并且请求类型为json
*
* @param url
* @param headerMap
* @param method
* @param obj
* @param tClass 为需要返回的实体类型
* @return
*/
public <T> T doHttp(String url, Map<String, String> headerMap, HttpMethod method, Object obj, Class<T> tClass) {
return doHttp(url, null, headerMap, method, obj, tClass);
}
/**
* @param url
* @param contentType
* @param headerMap
* @param method
* @param obj
* @param tClass 为需要返回的实体类型
* @return
*/
public <T> T doHttp(String url, MediaType contentType, Map<String, String> headerMap, HttpMethod method, Object obj, Class<T> tClass) {
HttpHeaders headers = new HttpHeaders();
contentType = contentType == null ? MediaType.APPLICATION_JSON : contentType;
headers.setContentType(contentType);
if (headerMap != null && headerMap.size() > 0) {
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
headers.add(entry.getKey(), entry.getValue());
}
}
HttpEntity entity = new HttpEntity(obj, headers);
ResponseEntity<T> exchange = restTemplate.exchange(url, method, entity, tClass);
return exchange.getBody();
}
/**
* 默认不带头,并且请求类型为json,返回List集合对象
*
* @param url
* @param method
* @param obj
* @param tClass
* @return
*/
public <T> List<T> doHttpList(String url, HttpMethod method, Object obj, Class<T> tClass) {
return doHttpList(url, MediaType.APPLICATION_JSON, null, method, obj, tClass);
}
/**
* 默认不带头,返回List集合对象
*
* @param url
* @param contentType
* @param method
* @param obj
* @param tClass 为需要返回的实体类型
* @return
*/
public <
T> List<T> doHttpList(String url, MediaType contentType, HttpMethod method, Object obj, Class<T> tClass) {
return doHttpList(url, contentType, null, method, obj, tClass);
}
/**
* 默认带头,并且请求类型为json,返回List集合对象
*
* @param url
* @param headerMap
* @param method
* @param obj
* @param tClass
* @return
*/
public <
T> List<T> doHttpList(String url, Map<String, String> headerMap, HttpMethod method, Object obj, Class<T> tClass) {
return doHttpList(url, null, headerMap, method, obj, tClass);
}
/**
* 返回List集合对象
*
* @param url
* @param contentType
* @param headerMap
* @param method
* @param obj
* @param tClass 为需要返回的实体类型
* @param <T>
* @return
*/
public <
T> List<T> doHttpList(String url, MediaType contentType, Map<String, String> headerMap, HttpMethod method, Object obj, Class<T> tClass) {
HttpHeaders headers = new HttpHeaders();
contentType = contentType == null ? MediaType.APPLICATION_JSON : contentType;
headers.setContentType(contentType);
if (headerMap != null && headerMap.size() > 0) {
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
headers.add(entry.getKey(), entry.getValue());
}
}
HttpEntity entity = new HttpEntity(obj, headers);
ParameterizedTypeReference<List<T>> responseType = new ParameterizedTypeReference<List<T>>() {
};
ResponseEntity<List<T>> exchange = restTemplate.exchange(url, method, entity, responseType);
// RestTemplate 会将List<obj>转为List<LinkedHashMap>
List<T> list = exchange.getBody();
return mapToEntity(list, tClass);
}
public static <T> List<T> mapToEntity(List<T> body, Class<T> clazz) {
List<T> list = new ArrayList<>();
if (body.size() <= 0) {
return list;
}
List<Map<String, Object>> mapList = new ArrayList<>();
for (T t : body) {
Map<String, Object> map = (Map<String, Object>) t;
mapList.add(map);
}
try {
Field[] declaredFields = clazz.getDeclaredFields();
for (Map<String, Object> map : mapList) {
T t = clazz.newInstance();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
String name = declaredField.getName();
if (null != map.get(name)) {
declaredField.set(t, map.get(name));
}
}
list.add(t);
}
} catch (Exception e) {
throw new RuntimeException("属性设置失败!");
}
return list;
}
}
3、GET请求
@RestController
public class Controller {
@RequestMapping("/test")
public String sendThymeleaf() {
// 请求URL(有参数就封装)
String url = "http://127.0.0.1:9898/other/test2?name=测试&age=123";
// 封装头
Map<String, String> headerMap = new HashMap<>();
headerMap.put("token", "123456");
// 设置响应实体,比如为User,那么传入User.class即可
User user = fileRequestServer.doHttp(url, MediaType.APPLICATION_JSON, headerMap, HttpMethod.GET, null, User.class);
System.out.println(user.toString());
}
}
4、POST请求
@RestController
public class Controller {
@RequestMapping("/test")
public String sendThymeleaf() {
String url = "http://127.0.0.1:9898/other/test2";
// POST请求时,参数可以为Map对象也可以为具体的实体对象,二选一
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "测试名称");
paramMap.put("gender", "男");
paramMap.put("age", 10);
// POST请求时,参数可以为Map对象也可以为具体的实体对象,二选一
// UserParam param= new UserParam ();
// param.setName("测试名称");
// param.setgender("男");
// param.setAge(10);
// 封装头
Map<String, String> headerMap = new HashMap<>();
headerMap.put("token", "123456");
// 设置响应实体,比如为User,那么传入User.class即可
User user = fileRequestServer.doHttp(url, MediaType.APPLICATION_JSON, headerMap, HttpMethod.POST, paramMap, User.class);
System.out.println(user.toString());
}
}
5、PUT请求
@RestController
public class Controller {
@RequestMapping("/test")
public String sendThymeleaf() {
String url = "http://127.0.0.1:9898/other/test2";
// PUT请求时,参数可以为Map对象也可以为具体的实体对象,二选一
// Map<String, Object> paramMap = new HashMap<>();
// paramMap.put("name", "测试名称");
// paramMap.put("gender", "男");
// paramMap.put("age", 10);
// PUT请求时,参数可以为Map对象也可以为具体的实体对象,二选一
UserParam param= new UserParam ();
param.setName("测试名称");
param.setgender("男");
param.setAge(10);
// 封装头
Map<String, String> headerMap = new HashMap<>();
headerMap.put("token", "123456");
// 设置响应实体,比如为User,那么传入User.class即可
User user = fileRequestServer.doHttp(url, MediaType.APPLICATION_JSON, headerMap, HttpMethod.PUT, param, User.class);
System.out.println(user.toString());
}
}
6、DELETE请求
@RestController
public class Controller {
@RequestMapping("/test")
public String sendThymeleaf() {
// 123456为DELETE请求时的参数,比如:test2/{id}
String url = "http://127.0.0.1:9898/other/test2/123456";
// DELETE请求时,如果需要body参数,那么可以为Map对象也可以为具体的实体对象,二选一
// Map<String, Object> paramMap = new HashMap<>();
// paramMap.put("name", "测试名称");
// paramMap.put("gender", "男");
// paramMap.put("age", 10);
// DELETE请求时,如果需要body参数,那么可以为Map对象也可以为具体的实体对象,二选一
// UserParam param= new UserParam ();
// param.setName("测试名称");
// param.setgender("男");
// param.setAge(10);
// 封装头
Map<String, String> headerMap = new HashMap<>();
headerMap.put("token", "123456");
// 设置响应实体,比如为User,那么传入User.class即可
// DELETE 如果不需要参数,那么obj传入null即可,如果需要body参数就传递响应参数
User user = fileRequestServer.doHttp(url, MediaType.APPLICATION_JSON, headerMap, HttpMethod.PUT, null, User.class);
System.out.println(user.toString());
}
}
7、List集合响应请求
当返回对象是一个List时,则使用doHttpList()方法;
@RestController
public class Controller {
@RequestMapping("/test")
public String sendThymeleaf() {
// 请求URL(有参数就封装)
String url = "http://127.0.0.1:9898/other/test2?name=测试&age=123";
// 封装头
Map<String, String> headerMap = new HashMap<>();
headerMap.put("token", "123456");
// 设置响应实体,比如为User,那么传入User.class即可
List<User> user = fileRequestServer.doHttpList(url, MediaType.APPLICATION_JSON, headerMap, HttpMethod.GET, null, User.class);
System.out.println(user.toString());
}
}
8、文件传递请求(可批量文件、可带参数)
@RestController
public class Controller {
@PostMapping("/test")
public void test(@RequestParam("files") MultipartFile[] file) throws IOException {
String url = "http://127.0.0.1:9898/other/test";
// 上传文件
String originalFilename = file.getOriginalFilename();
System.out.println("文件名称:" + originalFilename);
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
// 遍历多个MultipartFile对象;
for (MultipartFile file : files) {
ByteArrayResource resource = new ByteArrayResource(file.getBytes()) {
@Override
public String getFilename() {
return file.getOriginalFilename();
}
};
// 写入文件流:每遍历一次,就add一次
paramMap.add("files", resource);
}
// 其他参数
paramMap.add("name", "测试名称");
paramMap.add("age", 10);
// 封装头
Map<String, String> headerMap = new HashMap<>();
headerMap.put("token", "12333");
String s = fileRequestServer.doHttp(url, MediaType.MULTIPART_FORM_DATA, headerMap, HttpMethod.POST, paramMap, String.class);
System.out.println(s);
}
}
被调用方Controller层定义:如果在传入文件流的同时,还存在参数的话,那么参与需要使用@RequestParam进行定义;
public String test(@RequestParam("files") MultipartFile[] file, @RequestParam String name, @RequestParam Integer age){
......
......
}