RestTemplateUtil
1.添加httpclient依赖作为restTemplate的实现
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
2.配置
- 使用clientHttpRequestFactory, 可以设置超时时间, 连接池等等
- 使用自定义的fastJson转换器
- 使用自定义拦截器可以在每次发送请求时添加header等信息 ```java package com.bestser.factory.cloud;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.bestser.factory.config.security.oauth2.AuthorizationConstants; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.client.RestTemplate;
/**
- RestTemplate配置类 *
- @author gavin
- @date 2020-07-01
- 文档: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/remoting.html#rest-client-access
api: https://docs.spring.io/spring-framework/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html */ @Configuration public class RestTemplateConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfig.class); private StringRedisTemplate redisTemplate;
public RestTemplateConfig(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Bean public RestTemplate restTemplate(ClientHttpRequestInterceptor interceptor) {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
restTemplate.setInterceptors(Collections.singletonList(interceptor));
replaceJackson2FastJson(restTemplate);
return restTemplate;
}
@Bean public RestTemplate authorizedRestTemplate(ClientHttpRequestInterceptor interceptor) {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
restTemplate.setInterceptors(Collections.singletonList(interceptor));
replaceJackson2FastJson(restTemplate);
return restTemplate;
}
/**
替换默认的jackson转换器为fastJson转换器 */ private void replaceJackson2FastJson(RestTemplate restTemplate) { List
> converters = restTemplate.getMessageConverters(); //原有的String是ISO-8859-1编码 替换成 UTF-8编码 converters.removeIf(c -> c instanceof StringHttpMessageConverter); converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); converters.add(0, fastJsonHttpMessageConverter()); } /**
配置fastJson转换器 */ @Bean public HttpMessageConverter fastJsonHttpMessageConverter() { FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue, SerializerFeature.QuoteFieldNames,
SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.DisableCircularReferenceDetect);
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); fastJsonHttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8)); fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); return fastJsonHttpMessageConverter; }
/**
配置clientHttpRequestFactory */ @Bean public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() { try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
//设置连接池
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
//最大连接数
connectionManager.setMaxTotal(20);
//同路由并发数
connectionManager.setDefaultMaxPerRoute(10);
httpClientBuilder.setConnectionManager(connectionManager);
HttpClient httpClient = httpClientBuilder.build();
// httpClient连接配置
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpClient);
//连接超时
requestFactory.setConnectTimeout(60 * 1000);
//数据读取超时时间
requestFactory.setReadTimeout(60 * 1000);
//连接不够用的等待时间
requestFactory.setConnectionRequestTimeout(60 * 1000);
return requestFactory;
} catch (Exception e) {
LOGGER.error(String.format("初始化clientHttpRequestFactory失败, 错误信息: %s", e));
} return null; }
/**
- 自定义拦截器携带token和authorization
*/
@Bean
public ClientHttpRequestInterceptor authorizedRequestInterceptor() {
return (httpRequest, bytes, clientHttpRequestExecution) -> {
}; } }HttpHeaders headers = httpRequest.getHeaders();
String token = (String) SecurityContextHolder.getContext().getAuthentication().getCredentials();
if (!StringUtils.isBlank(token)) {
headers.add(AuthorizationConstants.TOKEN, token);
}
String authorization = redisTemplate.opsForValue()
.get(AuthorizationConstants.OAUTH2_TOKEN_KEY_PREFIX + token);
if (!StringUtils.isBlank(authorization)) {
headers.add(AuthorizationConstants.AUTHORIZATION, authorization);
}
return clientHttpRequestExecution.execute(httpRequest, bytes);
3.工具类, http请求的封装
```java
package com.bestser.factory.cloud;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
/**
* 发送http请求工具类
*
* @author gavin
* @date 2020-07-04
*/
@Component
public class RestTemplateUtil {
private final static Log LOGGER = LogFactory.getLog(RestTemplateUtil.class);
private final static int SUCCESS_CODE = 200;
private final static Object EMPTY_REQUEST_BODY = new Object();
private static RestTemplate rest;
public RestTemplateUtil(RestTemplate restTemplate) {
rest = restTemplate;
}
@SuppressWarnings("unchecked")
private static <T> CloudResponse<T> doCall(HttpMethod method, String url, Object requestBody) {
CloudResponse<T> response = new CloudResponse<>();
switch (method) {
case GET:
response = rest.getForObject(url, CloudResponse.class);
break;
case POST:
response = rest.postForObject(url, requestBody, CloudResponse.class);
break;
case PUT:
rest.put(url, requestBody);
response.setState(SUCCESS_CODE);
break;
case DELETE:
rest.delete(url);
response.setState(SUCCESS_CODE);
break;
default:
throw new RuntimeException(String.format("未知请求类型: %s", method));
}
return response;
}
/**
* 发送http请求
*
* @param method 请求方法
* @param url 请求url
* @param requestBody 请求体
* @param <T> 返回值的泛型
* @return T
*/
private static <T> T call(HttpMethod method, String url, Object requestBody) {
CloudResponse<T> response;
try {
response = doCall(method, url, requestBody);
LOGGER.info(String.format("url: %s, requestBody: %s, response: %s", url, JSONObject.toJSON(requestBody),
JSONObject.toJSON(response)));
} catch (Exception e) {
LOGGER.error(
String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
e.getMessage()));
throw e;
}
if (response.getState() != SUCCESS_CODE) {
LOGGER.error(
String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
response.getMessage()));
throw new RuntimeException(
String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
response.getMessage()));
}
return response.getData();
}
public static <T> T get(String url) {
return call(HttpMethod.GET, url, EMPTY_REQUEST_BODY);
}
public static <T> T post(String url) {
return call(HttpMethod.POST, url, EMPTY_REQUEST_BODY);
}
public static <T> T post(String url, Object requestBody) {
return call(HttpMethod.POST, url, requestBody);
}
public static void put(String url) {
call(HttpMethod.PUT, url, EMPTY_REQUEST_BODY);
}
public static void put(String url, Object requestBody) {
call(HttpMethod.PUT, url, requestBody);
}
public static void delete(String url) {
call(HttpMethod.DELETE, url, EMPTY_REQUEST_BODY);
}
public static void delete(String url, Object requestBody) {
call(HttpMethod.DELETE, url, requestBody);
}
}
优化版本(添加自定义请求头参数):
package com.bestser.factory.cloud;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
/**
* 发送http请求工具类
*
* @author gavin
* @date 2020-07-04
*/
@Component
public class RestTemplateUtil {
private final static Log LOGGER = LogFactory.getLog(RestTemplateUtil.class);
private final static int SUCCESS_CODE = 200;
private final static Object EMPTY_REQUEST_BODY = new Object();
private final static HttpHeaders EMPTY_REQUEST_HEADER = new HttpHeaders();
private static RestTemplate rest;
public RestTemplateUtil(RestTemplate restTemplate) {
rest = restTemplate;
}
/**
* 发送http请求
*
* @param method 请求方法
* @param url 请求url
* @param requestBody 请求体
* @param <T> 返回值的泛型
* @return T
*/
@SuppressWarnings("unchecked")
private static <T> T call(String url, HttpMethod method, HttpHeaders requestHeaders, Object requestBody) {
CloudResponse<T> response;
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
try {
response = rest.exchange(url, method, requestEntity, CloudResponse.class).getBody();
LOGGER.info(String.format("url: %s, requestBody: %s, response: %s", url, JSONObject.toJSON(requestBody),
JSONObject.toJSON(response)));
} catch (Exception e) {
LOGGER.error(
String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
e.getMessage()));
throw e;
}
if (response == null || response.getState() != SUCCESS_CODE) {
LOGGER.error(
String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
response == null ? "null" : response.getMessage()));
throw new RuntimeException(
String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
response == null ? "null" : response.getMessage()));
}
return response.getData();
}
public static <T> T get(String url) {
return call(url, HttpMethod.GET, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
}
public static <T> T get(String url, HttpHeaders httpHeaders) {
return call(url, HttpMethod.GET, httpHeaders, EMPTY_REQUEST_BODY);
}
public static <T> T post(String url) {
return call(url, HttpMethod.POST, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
}
public static <T> T post(String url, Object requestBody) {
return call(url, HttpMethod.POST, EMPTY_REQUEST_HEADER, requestBody);
}
public static void put(String url) {
call(url, HttpMethod.PUT, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
}
public static void put(String url, Object requestBody) {
call(url, HttpMethod.PUT, EMPTY_REQUEST_HEADER, requestBody);
}
public static void delete(String url) {
call(url, HttpMethod.DELETE, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
}
}
4.标准返回类
package com.bestser.factory.cloud;
import lombok.Data;
/**
* @author gavin
* @date 2020-07-04
*/
@Data
public class CloudResponse<T> {
private Integer state;
private String message;
private T data;
}
CloudUrlBuilder
1.url构建工具类
package com.bestser.factory.cloud;
import java.util.Collections;
import java.util.Map;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
/**
* cloud端url构建器
*
* @author gavin
* @date 2020-07-03
*/
@Component
public class CloudUrlBuilder {
private static final String HTTP = "http";
private static final String AUTH_HOST = "localhost:8612";
private static final String INVENTORY_HOST = "localhost:8610";
/**
* 构建url
*
* @param host 主机ip:端口
* @param path 路径
* @param params 请求参数
* @param args 路径参数
* @return url
*/
private static String buildUrl(String host, CloudResource path, Map<String, Object> params, String... args) {
UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(HTTP).host(host)
.path(path.getPath());
if (!CollectionUtils.isEmpty(params)) {
params.forEach(builder::queryParam);
}
UriComponents build = builder.build();
if (args != null && args.length > 0) {
return build.expand(args).toUriString();
}
return build.toUriString();
}
public static String buildAuthUrl(CloudResource path, String... args) {
return buildAuthUrl(path, Collections.emptyMap(), args);
}
public static String buildAuthUrl(CloudResource path, Map<String, Object> params) {
return buildAuthUrl(path, params, null);
}
public static String buildAuthUrl(CloudResource path, Map<String, Object> params, String... args) {
return buildUrl(AUTH_HOST, path, params, args);
}
public static String buildInventoryUrl(CloudResource path, String... args) {
return buildInventoryUrl(path, Collections.emptyMap(), args);
}
public static String buildInventoryUrl(CloudResource path, Map<String, Object> params) {
return buildInventoryUrl(path, params, null);
}
public static String buildInventoryUrl(CloudResource path, Map<String, Object> params, String... args) {
return buildUrl(INVENTORY_HOST, path, params, args);
}
}
2.url资源路径的定义
package com.bestser.factory.cloud;
/**
* cloud端资源路径
*
* @author gavin
* @date 2020-07-01
*/
public enum CloudResource {
/**
* oauth2令牌
*/
OAUTH2_TOKEN("oauth2令牌", "/oauth/token"),
/**
* 客户端权限
*/
CLIENT_PRIVILEGE("客户端权限", "/auth/{serviceNames}"),
/**
* 配件
*/
PART("配件", "/product/part"),
PART_VALUE("配件值", "/product/part/{propertyValueId}"),
PART_LIST("配件列表", "/product/part/list");
private final String caption;
private final String path;
CloudResource(String caption, String path) {
this.caption = caption;
this.path = path;
}
public String caption() {
return this.caption;
}
public String getPath() {
return path;
}
}
使用示例
调用方
@RestController
@ResponseResult
@RequestMapping("/api/setting/part")
public class PartSettingController {
@Autowired
private DesignPartService designPartService;
@GetMapping("/list")
public List<ProductPropertyVO> listParts() {
return RestTemplateUtil.get(CloudUrlBuilder.buildInventoryUrl(CloudResource.PART_LIST));
}
@PostMapping
public void createParts(@RequestBody ProductPropertyVO vo) {
if (StringUtils.isBlank(vo.getPropertyValue())) {
throw new RestMessageException(ResponseStateEnum.PARAM_ERROR, "配件名称不能为空");
}
RestTemplateUtil.post(CloudUrlBuilder.buildInventoryUrl(CloudResource.PART), vo);
}
@PutMapping
@Transactional(rollbackFor = Exception.class)
public void updatePart(@RequestBody ProductPropertyVO vo) {
if (StringUtils.isBlank(vo.getPropertyValue()) || StringUtils.isBlank(vo.getNewPropertyValue())) {
throw new RestMessageException(ResponseStateEnum.PARAM_ERROR, "配件名称不能为空");
}
// 修改 design_part 表的配件名称
designPartService.updatePartName(vo.getPropertyValue(), vo.getNewPropertyValue());
RestTemplateUtil.put(CloudUrlBuilder.buildInventoryUrl(CloudResource.PART), vo);
}
@DeleteMapping
@Transactional(rollbackFor = Exception.class)
public void deletePart(@RequestBody ProductPropertyVO vo) {
if (designPartService.isPartUsed(vo.getPropertyValue())) {
throw new RestMessageException(ResponseStateEnum.PARAM_ERROR, "配件已被使用, 不能删除");
}
RestTemplateUtil.delete(CloudUrlBuilder
.buildInventoryUrl(CloudResource.PART_VALUE, String.valueOf(vo.getPropertyValueId())));
RestTemplateUtil.delete(CloudUrlBuilder
.buildInventoryUrl(CloudResource.PART_VALUE, String.valueOf(vo.getPropertyValueId())));
}
}
被调用方
@ApiOperation("获取配件列表")
@GetMapping("/part/list")
public List<PropertyValueVO> listParts() {
return productService.listPropertiesByName("配件名称");
}
@ApiOperation("新增配件, 参数中propertyValue必填")
@PostMapping("/part")
public void createParts(@RequestBody ProductPropertyParam param) {
if (StringUtils.isBlank(param.getPropertyValue())) {
throw new ServiceException("配件名称不能为空");
}
productService.createPart(param.getPropertyValue());
}
@ApiOperation("修改配件名称, 参数中propertyValueId和propertyValue必填")
@PutMapping("/part")
public void updatePart(@RequestBody ProductPropertyParam param) {
if (StringUtils.isBlank(param.getPropertyValue())) {
throw new ServiceException("配件名称不能为空");
}
productService.updateProductPropertyValue(param.getPropertyValueId(), param.getNewPropertyValue());
}
@ApiOperation("删除配件, 参数中propertyValueId必填")
@DeleteMapping("/part/{propertyValueId}")
public void deletePart(@PathVariable("propertyValueId") Integer propertyValueId) {
productService.deleteProductPropertyValue(propertyValueId);
}