世面上有很多很多好用的客戶端,如HttpClient,OkHttp等,如果您已經用慣了其他的客戶端,那么可以繼續用,如果您在編寫Http請求的時候,感覺要寫的代碼很散,或者要寫的內容很多,很復雜,那么您可以嘗試一下RestBuilder,他設計的初衷就是通過傻瓜式鏈式調用,完成http請求。
- hello RestBuilder
發送一個非常簡單的http請求
String response =
RestBuilder
.builder()
.sendForObj(HttpMethod.GET, "http://www.baidu.com", String.class);
- get請求路徑url參數拼接
String response =
RestBuilder
.builder()
.pathParam("age","10")
.pathParam("name","xiaoming")
.sendForObj(HttpMethod.GET, "http://www.baidu.com", String.class);
拼接后的url
- 基于restful風格的參數拼接
String response =
RestBuilder
.builder()
.restfulPathParam("age","10")
.restfulPathParam("name","xiaoming")
.sendForObj(HttpMethod.GET, "http://www.baidu.com/${age}/${name}", String.class);
拼接后的url
- 設置
Content-type
&添加Header
String response =
RestBuilder
.builder()
.contentType(MediaType.APPLICATION_JSON)
.header("token","xxxxxxxxxxxxxxx")
.sendForObj(HttpMethod.GET, "http://www.baidu.com", String.class);
- 表單請求
String response =
RestBuilder
.builder()
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.formData("username","xiaoming")
.formData("password","123456")
.sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
http請求信息
HTTP POST http://www.baidu.com
Accept=[text/plain, application/json, application/*+json, /]
Writing [{username=[xiaoming], password=[123456]}] as "application/x-www-form-urlencoded"
-
添加body
-
方式一
不斷添加k,v參數,最終會整合成一個json對象
String response = RestBuilder .builder() .contentType(MediaType.APPLICATION_JSON) .bodyParam("username","xiaoming") .bodyParam("password","123456") .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
http請求信息
HTTP POST http://www.baidu.com
Accept=[text/plain, application/json, application/*+json, /]
Writing [{"password":"123456","username":"xiaoming"}] as "application/json"-
方式二
直接傳入對象,如果是String類型,直接當做body,如果是其他類型,最終轉換成json形式
String response = RestBuilder .builder() .contentType(MediaType.APPLICATION_JSON) .bodyObj(() -> User.builder() .name("xiaoming") .password("123456") .build()) .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
- 添加監控日志
調用鏈中加入
monitor()
方法String response = RestBuilder .builder() .contentType(MediaType.APPLICATION_JSON) .bodyObj(() -> User.builder() .name("xiaoming") .password("123456") .build()) .monitor() .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
- 設置
readTimeout&connectTimeout
String response = RestBuilder .builder() .timeout(2000,2000) .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
-
獲取響應的Http狀態碼,響應頭等信息
將
sendForObj
換成sendForEntity
ResponseEntity<String> responseEntity = RestBuilder .builder() .timeout(2000, 2000) .sendForEntity(HttpMethod.POST, "http://www.baidu.com", String.class);
-
獲取響應流
一般用于文件下載使用,將
sendForObj
換成sendForInputStream
即可。
InputStream inputStream = RestBuilder .builder() .timeout(2000, 2000) .sendForInputStream(HttpMethod.POST, "http://www.baidu.com");
-
復雜響應類型
獲取的響應類型形如:
Map<String,<List<String>>>
形式,這時我們可以使用JDK提供的TypeToken
設置響應類型
Type type = new TypeToken<HashMap<String, List<String>>>() {}.getType(); HashMap<String, List<String>> response = RestBuilder .builder() .timeout(2000,2000) .sendForObj(HttpMethod.POST, "http://www.baidu.com", type);
-
[注]
- 底層封裝的是RestTemplate,使用到的所有Http相關API均來自Spring
- RestTemplate不會頻繁的被創建,如果不設置超時時間,那么使用默認的restTemplate對象,如果設置了超時時間會嘗試從緩存池中獲取restTemplate對象,如果不存在,才會創建restTempalte對象。
- 這里需要添加gson依賴,用于json處理,
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
- 代碼
@Slf4j
public class RestBuilder {
private static final RestTemplate REST_TEMPLATE;
private HttpHeaders httpHeaders = new HttpHeaders();
private String postBodyStr;
private Map<String, String> restfulPathParamMap;
private Map<String, String> getParamMap;
private Map<String, Object> bodyParam;
private MultiValueMap<String, Object> formData;
private HttpEntity<?> httpEntity;
private String requestPath;
private Integer readTimeout;
private Integer connectTimeout;
private Boolean setTimeout = false;
private Boolean monitor = false;
private static final Cache<String, RestTemplate> CACHE;
private static final Object LOCK = new Object();
static {
REST_TEMPLATE = new RestTemplate();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
REST_TEMPLATE.getMessageConverters().add(converter);
CACHE = CacheBuilder.newBuilder()
.maximumSize(50)
.expireAfterWrite(30, TimeUnit.MINUTES)
.build();
}
private RestBuilder() {
}
public static RestBuilder builder() {
return new RestBuilder();
}
/**
* 添加header
*
* @param headerKey key
* @param value value
* @return {@link RestBuilder}
*/
public RestBuilder header(String headerKey, String value) {
Assert.hasText(headerKey, "headerKey must not be null");
httpHeaders.add(headerKey, value);
return this;
}
/**
* 設置Http ContentType
*
* @param mediaType {@link MediaType}
* @return {@link RestBuilder}
*/
public RestBuilder contentType(MediaType mediaType) {
if (Objects.isNull(httpHeaders)) {
httpHeaders = new HttpHeaders();
}
httpHeaders.setContentType(mediaType);
return this;
}
/**
* 設置GET請求參數,URL拼接形式
*
* @param pathParam key
* @param value value
* @return {@link RestBuilder}
*/
public RestBuilder pathParam(String pathParam, String value) {
Assert.hasText(pathParam, "supplier must not be null");
if (Objects.isNull(getParamMap)) {
getParamMap = new HashMap<>();
}
getParamMap.put(pathParam, value);
return this;
}
/**
* 設置post表單
*
* @param param key
* @param value value
* @return {@link RestBuilder}
*/
public RestBuilder formData(String param, String value) {
Assert.hasText(param, "supplier not be null");
if (Objects.isNull(this.formData)) {
formData = new LinkedMultiValueMap<>();
}
this.formData.add(param, value);
return this;
}
/**
* 設置body參數,最終會被轉換成json
*
* @param key key
* @param value value
* @return {@link RestBuilder}
*/
public <T> RestBuilder bodyParam(String key, String value) {
Assert.hasText(key, "function must not be null");
if (Objects.isNull(bodyParam)) {
this.bodyParam = new HashMap<>();
}
bodyParam.put(key, value);
return this;
}
/**
* 設置請求body,最終會轉換成json
*
* @param supplier {@link Object}
* @return {@link RestBuilder}
*/
public RestBuilder bodyObj(Supplier<Object> supplier) {
Assert.notNull(supplier, "supplier must not be null");
Object obj = supplier.get();
this.postBodyStr = obj instanceof String ? (String) obj : GsonUtils.object2Json(obj);
return this;
}
/**
* 設置路徑參數
*
* @param key key
* @param value value
* @return {@link RestBuilder}
*/
public RestBuilder restfulPathParam(String key, String value) {
Assert.hasText(key, "key must not be null");
if (Objects.isNull(restfulPathParamMap)) {
this.restfulPathParamMap = new HashMap<>();
}
restfulPathParamMap.put(key, value);
return this;
}
/**
* 發送請求并返回{@link ResponseEntity}
*
* @param requestPath 請求路徑/url
* @param responseType 響應類型
* @param <R> ResponseType
* @return response
*/
public <R> ResponseEntity<R> sendForEntity(HttpMethod requestMethod, String requestPath, Type responseType) {
build(requestPath);
return requestForEntity(this.requestPath, this.httpEntity, responseType, requestMethod);
}
/**
* 發送請求并直接返回響應體
*
* @param requestMethod {@link HttpMethod}
* @param requestPath 請求路徑
* @param responseType 響應類型
* @param <R> R
* @return response Body
*/
public <R> R sendForObj(HttpMethod requestMethod, String requestPath, Type responseType) {
ResponseEntity<R> responseEntity = sendForEntity(requestMethod, requestPath, responseType);
return responseEntity.getBody();
}
/**
* 發送請求并返回{@link ResponseEntity}
*
* @param requestPath 請求路徑/url
* @param responseType 響應類型
* @param <R> ResponseType
* @return response
*/
public <R> ResponseEntity<R> sendForEntity(HttpMethod requestMethod, String requestPath, Class<R> responseType) {
build(requestPath);
return requestForEntity(this.requestPath, this.httpEntity, responseType, requestMethod);
}
/**
* 發送請求并直接返回響應體
*
* @param requestMethod {@link HttpMethod}
* @param requestPath 請求路徑
* @param responseType 響應類型
* @param <R> R
* @return response Body
*/
public <R> R sendForObj(HttpMethod requestMethod, String requestPath, Class<R> responseType) {
ResponseEntity<R> responseEntity = sendForEntity(requestMethod, requestPath, responseType);
return responseEntity.getBody();
}
/**
* 發送GET請求并反返回響應流
*
* @param requestPath 請求路徑/URL
* @return InputStream
*/
public InputStream sendForInputStream(HttpMethod requestMethod, String requestPath) throws IOException {
ResponseEntity<Resource> responseEntity = sendForEntity(requestMethod, requestPath, Resource.class);
return Objects.requireNonNull(responseEntity.getBody()).getInputStream();
}
/**
* 發送請求檢測
*
* @return this
*/
public RestBuilder monitor() {
this.monitor = true;
return this;
}
/**
* 設置readTimeOut
*
* @param connectTimeout connectTimeout
* @param readTimeout readTimeout
* @return {@link RestBuilder}
*/
public RestBuilder timeout(int readTimeout, int connectTimeout) {
this.setTimeout = true;
this.readTimeout = readTimeout;
this.connectTimeout = connectTimeout;
return this;
}
/**
* 構造restful路徑
*
* @param path path
* @return restful path
*/
private String generatePath(String path) {
if (Objects.nonNull(restfulPathParamMap) && !restfulPathParamMap.isEmpty()) {
// 替換restful值
Set<Map.Entry<String, String>> entrySet = restfulPathParamMap.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
path = path.replace(String.format("${%s}", entry.getKey()), entry.getValue());
}
}
if (Objects.nonNull(getParamMap) && !getParamMap.isEmpty()) {
StringBuilder pathBuilder = new StringBuilder(path).append("?");
// 拼接請求值
getParamMap.forEach((k, v) -> pathBuilder.append(k).append("=").append(v).append("&"));
// 最后一個&
int length = pathBuilder.length();
pathBuilder.delete(length - 1, length);
path = pathBuilder.toString();
}
if (monitor) {
log.info("PATH [ {} ]", path);
}
return path;
}
/**
* 構造http的URL和body
*
* @param path 請求路徑
*/
private void build(String path) {
// 構造請求路徑
this.requestPath = generatePath(path);
Object body = null;
// 表單和body只能選中一個
Assert.isTrue(!(formData != null && (postBodyStr != null || bodyParam != null)),
"body or form data only one can be selected");
// 沒有指定contentType默認'application/json'
MediaType contentType = httpHeaders.getContentType();
if (Objects.isNull(formData)) {
if (Objects.isNull(contentType)) {
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
}
body = Strings.isNullOrEmpty(postBodyStr) ? GsonUtils.object2Json(bodyParam) : postBodyStr;
} else {
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
body = formData;
}
// body允許傳空body
this.httpEntity = new HttpEntity<>(body, httpHeaders);
if (this.monitor) {
log.info("requestEntity [ {} ]", httpEntity.toString());
}
}
private <R> ResponseEntity<R> requestForEntity(String url, @Nullable Object request, Type responseType, HttpMethod httpMethod, Object... uriVariables) throws RestClientException {
long begin = 0;
if (monitor) {
begin = System.currentTimeMillis();
}
RestTemplate restTemplate = getRestTemplate();
RequestCallback requestCallback = restTemplate.httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<R>> responseExtractor = restTemplate.responseEntityExtractor(responseType);
ResponseEntity<R> responseEntity = nonNull(restTemplate.execute(url, httpMethod, requestCallback, responseExtractor, uriVariables));
if (monitor) {
log.info("Response Message [{}]", responseEntity);
log.info("cost time [{}] ms.", System.currentTimeMillis() - begin);
}
return responseEntity;
}
private <T> T nonNull(@Nullable T result) {
Assert.state(result != null, "No result");
return result;
}
/**
* 獲取restTemplate
*
* @return RestTemplate
*/
private RestTemplate getRestTemplate() {
if (!setTimeout) {
return RestBuilder.REST_TEMPLATE;
}
synchronized (LOCK) {
// 先去查看是否已經緩存了相同設置超時時間的restTemplate
// 拼接規則為 ${readTimeout.toString()}:${connectTimeout.toString()}
String cacheKey = generateCacheKey();
RestTemplate timoutRestTemplate = CACHE.getIfPresent(cacheKey);
if (Objects.nonNull(timoutRestTemplate)) {
// 重置超時時間
CACHE.put(cacheKey, timoutRestTemplate);
return timoutRestTemplate;
}
// 之前沒有緩存該restTemplate,生成好restTemplate,然后緩存起來
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
if (Objects.nonNull(this.readTimeout)) {
requestFactory.setReadTimeout(readTimeout);
}
if (Objects.nonNull(this.connectTimeout)) {
requestFactory.setConnectTimeout(connectTimeout);
}
RestTemplate restTemplate = new RestTemplate(requestFactory);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
restTemplate.getMessageConverters().add(converter);
CACHE.put(cacheKey, restTemplate);
return restTemplate;
}
}
private String generateCacheKey() {
StringBuilder sb = new StringBuilder();
if (Objects.nonNull(readTimeout)) {
sb.append(readTimeout.toString());
}
if (Objects.nonNull(connectTimeout)) {
sb.append(connectTimeout.toString());
}
return sb.toString();
}
}