RestTemplate:Spring 提供的一个同步的 Rest API 客户端。提供高度封装的接口,可以让我们非常方便地进行 Rest API 调用,大大提高客户端的编写效率。
Rest 类型 | 方法名 | 描述 |
---|---|---|
DELETE | delete() | 执行HTTP DELETE操作 |
GET | getForObject() | 执行HTTP GET请求,将响应body封装成指定对象返回 |
getForEntity() | 执行HTTP GET请求,并将响应作为ResponseEntity返回。ResponseEntity包含了http响应状态码、响应头、响应体。 | |
POST | postForLocation() | 执行HTTP POST请求,并返回Location标头的返回值。 |
postForObject() | 执行HTTP POST请求,将响应body封装成指定对象返回 | |
postForEntity() | 执行HTTP POST请求,并将响应作为ResponseEntity返回。ResponseEntity包含了http响应状态码、响应头、响应体。 | |
PUT | put() | 执行HTTP PUT请求,返回void |
any | exchange() | 执行指定的HTTP方法,并将响应作为ResponseEntity返回。 |
execute() | 执行指定的HTTP方法,将响应body封装成指定对象返回 |
配置及使用
1.maven中引入jar包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.配置类:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
// 创建http client工厂类,底层client使用JDK的 HttpURLConnection。
// 默认没有连接池的概念,如有需要可换成Apache HttpClient或okhttp等其它http客户端
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// 连接超时设置,毫秒,默认-1
requestFactory.setConnectTimeout(30 * 1000);
// 读取超时设置,毫秒,默认-1
requestFactory.setReadTimeout(60 * 1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
// String类型的编解码设置
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
}
3.使用:
@Service
public class RestTemplateTest {
// tips:响应的数据,restTemplate默认无法识别的类型,会转化为LinkedHashMap
@Resource
private RestTemplate restTemplate;
@PostConstruct
public void init() {
test1();
}
public void test1() {
// GET
Map<String, Object> mapGet = new HashMap<>();
mapGet.put("pageNum", 1); //替换url上的占位符 {pageNum}
mapGet.put("pageSize", 10); //替换url上的占位符 {pageSize}
String userRestString = restTemplate.getForObject("http://127.0.0.1:8080/users/{pageNum}/{pageSize}", String.class, mapGet);
BaseResponse<List<User>> listBaseResponse = JSONObject.parseObject(userRestString, new TypeReference<BaseResponse<List<User>>>() {});
String userString = restTemplate.getForObject("http://127.0.0.1:8080/users2?pageNum={pageNum}&pageSize={pageSize}", String.class, mapGet);
BaseResponse<List<User>> listBaseResponse1 = JSONObject.parseObject(userString, new TypeReference<BaseResponse<List<User>>>() {});
// POST
Page page = new Page(); //请求body
page.setPageNum(2);
page.setPageSize(5);
String userStringPost = restTemplate.postForObject("http://127.0.0.1:8080/usersByPost", page, String.class);
BaseResponse<List<User>> listBaseResponsePost = JSONObject.parseObject(userStringPost, new TypeReference<BaseResponse<List<User>>>() {});
// header
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
requestHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
requestHeaders.set(HttpHeaders.COOKIE, "language=cn;");
// body
// Map<String, Object> body = new HashMap<>();
// body.put("pageNum", 3);
// body.put("pageSize", 4);
// HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, requestHeaders);
// or body
Page pageBody = new Page();
pageBody.setPageNum(3);
pageBody.setPageSize(4);
HttpEntity<Page> entity = new HttpEntity<>(pageBody, requestHeaders);
// post
String stringPostForObject = restTemplate.postForObject("http://127.0.0.1:8080/usersByPost", entity, String.class);
BaseResponse<List<User>> listBaseResponse2 = JSONObject.parseObject(stringPostForObject, new TypeReference<BaseResponse<List<User>>>() {});
// getForEntity()
// ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:8080/users/{pageNum}/{pageSize}", String.class, mapGet);
// String userRestString = responseEntity.getBody();
// exchange()
// ResponseEntity<String> exchange = restTemplate.exchange("http://127.0.0.1:8080/usersByPost", HttpMethod.POST, entity, String.class);
// String exchangeBody = exchange.getBody();
// ResponseEntity<String> getExchange = restTemplate.exchange("http://127.0.0.1:8080/users", HttpMethod.GET, entity, String.class);
}
}
简单的工具类封装:
@Slf4j
public class HttpUtils {
private static final RestTemplate restTemplate;
static {
// 创建http client工厂类,底层client使用JDK的 HttpURLConnection。
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// 连接超时设置,毫秒,默认-1
requestFactory.setConnectTimeout(30 * 1000);
// 读取超时设置,毫秒,默认-1
requestFactory.setReadTimeout(60 * 1000);
restTemplate = new RestTemplate(requestFactory);
// String类型的编解码设置
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
/**
* GET请求
*
* @param url URL
*
* @return 响应body
*
* @throws RestClientException
*/
public static String get(String url) throws RestClientException {
return restTemplate.getForObject(url, String.class);
}
/**
* GET请求
*
* @param url 含占位符的URL,例:"http://127.0.0.1:8080/users?pageNum={pageNum}&pageSize={pageSize}"
* @param uriVariables 替换占位符变量的值
*
* @return 响应body
*
* @throws RestClientException
*/
public static String get(String url, Map<String, String> uriVariables) throws RestClientException {
return restTemplate.getForObject(url, String.class, uriVariables);
}
public static String get(String url, HttpHeaders headers) throws RestClientException {
return get(url, null, headers);
}
public static String get(String url, Map<String, String> uriVariables, HttpHeaders headers) throws RestClientException {
HttpEntity<Object> httpEntity = new HttpEntity<>(null, headers);
ResponseEntity<String> responseEntity;
if (uriVariables == null) {
responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
} else {
responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, uriVariables);
}
return responseEntity.getBody();
}
public static String post(String url, Object requestBody) throws RestClientException {
return post(url, requestBody, new HttpHeaders());
}
public static String post(String url, Object requestBody, HttpHeaders headers) throws RestClientException {
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
HttpEntity<Object> entity = new HttpEntity<>(requestBody, headers);
return restTemplate.postForObject(url, entity, String.class);
}
}
提供http服务的代码:
@Slf4j
@RestController
@RequestMapping("/")
public class RestServerController {
@GetMapping("/users/{pageNum}/{pageSize}")
public BaseResponse<List<User>> httpRestGet(@PathVariable Integer pageNum, @PathVariable Integer pageSize) {
log.info("pageNum={},pageSize={}", pageNum, pageSize);
List<User> list = new ArrayList<>();
User user1 = new User();
user1.setName("张三");
user1.setAge(20);
list.add(user1);
User user2 = new User();
user2.setName("李四");
user2.setAge(21);
list.add(user2);
return BaseResponse.success(list);
}
@GetMapping("/users2")
public BaseResponse<List<User>> httpGet(@RequestParam Integer pageNum, @RequestParam Integer pageSize) {
log.info("pageNum={},pageSize={}", pageNum, pageSize);
List<User> list = new ArrayList<>();
User user1 = new User();
user1.setName("张三2");
user1.setAge(22);
list.add(user1);
User user2 = new User();
user2.setName("李四2");
user2.setAge(32);
list.add(user2);
return BaseResponse.success(list);
}
@PostMapping("/usersByPost")
public BaseResponse<List<User>> postTest(@RequestBody Page page, HttpServletRequest request) {
log.info("page={}", page);
log.info("request={}", request.getHeaderNames());
List<User> list = new ArrayList<>();
User user1 = new User();
user1.setName("狗子");
user1.setAge(30);
list.add(user1);
User user2 = new User();
user2.setName("狗娃");
user2.setAge(31);
list.add(user2);
return BaseResponse.success(list);
}
}
@Data
public class User implements Serializable {
private static final long serialVersionUID = -3581380362378545843L;
private String name;
private Integer age;
}
@Data
public class Page {
private Integer pageNum;
private Integer pageSize;
}
@Data
public class BaseResponse<T> implements Serializable {
private static final long serialVersionUID = -619135033544136919L;
private Integer code;
private String msg;
private T data;
public static <T> BaseResponse<T> success(T data) {
BaseResponse<T> response = new BaseResponse<T>();
response.setCode(200);
response.setMsg("success");
response.setData(data);
return response;
}
}
4.RestTemplate + okhttp
RestTemplate底层的http client使用okhttp工具类提供的 OkHttpClient,替代默认的JDK的HttpURLConnection。
1、引入jar
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.2</version>
</dependency>
2、配置类
@Configuration
public class RestTemplateConfig {
/**
* 基于OkHttp3配置RestTemplate
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
3、okhttp简介
OkHttp 是一个默认高效的 HTTP 客户端:
- HTTP/2 支持允许对同一主机的所有请求共享一个套接字。
- 连接池减少了请求延迟(如果 HTTP/2 不可用)。
- 透明 GZIP 可缩小下载大小。
- 响应缓存避免完全重复的网络请求。
OkHttp在网络出现问题时坚持:它会从常见的连接问题中默默恢复。如果您的服务有多个 IP 地址,则 OkHttp 将在第一次连接失败时尝试备用地址。这对于在冗余数据中心托管的 IPv4+IPv6 和服务是必需的。OkHttp 支持现代 TLS 功能(TLS 1.3、ALPN、证书锁定)。它可以配置为回退以实现广泛的连接。
使用 OkHttp 很容易。它的请求/响应 API 设计有流畅的构建器和不变性。它支持同步阻塞调用和带有回调的异步调用。
RestTemplate VS WebClient
通过 RestTemplate,我们可以非常方便的进行 Rest API 调用。但是在 Spring 5 中提供了支持异步调用的 WebClient。
RestTemplate 使用 Java Servlet API,因此是同步和阻塞的。相反,WebClient 是异步的,在等待响应返回时不会阻塞正在执行的线程。只有当程序就绪时,才会产生通知。
RestTemplate 仍将会被使用。但在某些情况下,与阻塞方法相比,非阻塞方法使用的系统资源要少得多。因此,在这些情况下,WebClient 不失为是更好的选择。