当使用SpringBoot时,如果需要调用第三方Rest API,通常会使用RestTemplate。有时候偶尔搞不清楚参数要如何传递,明明参数已经赋值,对方接收到的确是空值。本文对经常使用的方式,做一下汇总。
准备工作
请求对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private Long id;
private String name;
}
第三方请求
@RestController
public class PersonController {
// 存储一下id和person的关系
private static Map<Long, Person> persons = new HashMap<>();
// 生成person的id
private static final LongAdder ID_ADDER = new LongAdder();
static {
ID_ADDER.add(1L);
Person person = new Person();
person.setId(ID_ADDER.longValue());
person.setName("1-person");
persons.put(person.getId(), person);
}
...省略其他
}
对于第三方接口仅简单实现,忽略一些诸如参数校验的细节问题。
RestTemplate访问get接口
请求参数在路径中
第三方接口,根据id查询Person
@GetMapping("/getPerson/{id}")
public Person getPersonPathVariable(@PathVariable(name = "id") Long id) {
return persons.getOrDefault(id, new Person());
}
请求示例
// 请求Get
URI uri = UriComponentsBuilder.fromUriString(String.format(URL_FORMAT, "/getPerson/{id}")).build(1L);
ResponseEntity<Person> forEntity = REST_TEMPLATE.getForEntity(uri, Person.class);
log.info("getForEntity result:{}", forEntity);
Person forObject = REST_TEMPLATE.getForObject(uri, Person.class);
log.info("getForObject result:{}", forObject);
ResponseEntity除了正常返回结果,还包含HTTP相关了一些信息,具体可以看下面的日志。
16:10:48.391 [main] INFO com.yichao.myblogs.resttemplate.RestTemplateTest - getForEntity result:<200,Person(id=1, name=1-person),[Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Sat, 15 May 2021 08:10:48 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>
通用的请求参数传递,即?name=xxx&id=xxx
第三方接口,根据id查询Person
@GetMapping("/getPerson")
public Person getPersonWithParams(HttpServletRequest httpServletRequest) {
String idStr = httpServletRequest.getParameter("id");
return persons.getOrDefault(Long.valueOf(idStr), new Person());
}
请求示例
// 请求Get
URI uri = UriComponentsBuilder.fromUriString(String.format(URL_FORMAT, "/getPerson/{id}")).build(1L);
uri = UriComponentsBuilder.fromUriString(String.format(URL_FORMAT, "/getPerson?id={id}")).build(1L);
ResponseEntity<Person> forEntity = REST_TEMPLATE.getForEntity(uri, Person.class);
log.info("getForEntity result:{}", forEntity);
Person forObject = REST_TEMPLATE.getForObject(uri, Person.class);
log.info("getForObject result:{}", forObject);
RestTemplate访问post接口
下面以API参数的几种不同形式进行区分。
参数使用@RequestBody
第三方接口,将传入的Person存储,并返回添加了主键的Person对象。
@PostMapping("/add/requestBody")
public Person postRequestBody(@RequestBody Person person) {
ID_ADDER.add(1L);
Long id = ID_ADDER.longValue();
person.setId(id);
persons.put(person.getId(), person);
return person;
}
请求示例
String url = String.format(URL_FORMAT, "/add/requestBody");
Person person;
ResponseEntity<Person> responseEntity;
// 生成一个仅包含name的对象,由第三方接口负责生成id,并返回
Person mockPerson = mockPerson();
person = REST_TEMPLATE.postForObject(url, mockPerson, Person.class);
log.info("requestBody postForObject result:{}", person);
responseEntity = REST_TEMPLATE.postForEntity(url, mockPerson, Person.class);
log.info("requestBody postForEntity result:{}", responseEntity);
直接使用对象
第三方接口
@PostMapping("/add/form")
public Person postForm(Person person) {
ID_ADDER.add(1L);
Long id = ID_ADDER.longValue();
person.setId(id);
persons.put(person.getId(), person);
return person;
}
请求示例
// form 请求格式
String url = String.format(URL_FORMAT, "/add/form");
HttpHeaders headers = new HttpHeaders();
Person mockPerson = mockPerson();
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
// 将请求对象转为map,并添加到multiValueMap中
@SuppressWarnings("unchecked")
Map<String, Object> params = JSON.parseObject(JSON.toJSONString(mockPerson), Map.class);
params.forEach(map::add);
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(map, headers);
Person person = REST_TEMPLATE.postForObject(url, httpEntity, Person.class);
log.info("form postForObject result:{}", person);
ResponseEntity<Person> responseEntity = REST_TEMPLATE.postForEntity(url, httpEntity, Person.class);
log.info("form postForEntity result{}", responseEntity);
使用HttpServletRequest
第三方接口
@PostMapping("/add/requestParam")
public Person postRequestParam(HttpServletRequest httpServletRequest) {
ID_ADDER.add(1L);
String name = httpServletRequest.getParameter("name");
Long id = ID_ADDER.longValue();
Person person = new Person();
person.setId(id);
person.setName(name);
persons.put(person.getId(), person);
return person;
}
请求示例
该方法同form的请求方式一致
String url = String.format(URL_FORMAT, "/add/requestParam");
HttpHeaders headers = new HttpHeaders();
Person mockPerson = mockPerson();
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
// 将请求对象转为map,并添加到multiValueMap中
@SuppressWarnings("unchecked")
Map<String, Object> params = JSON.parseObject(JSON.toJSONString(mockPerson), Map.class);
params.forEach(map::add);
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(map, headers);
Person person = REST_TEMPLATE.postForObject(url, httpEntity, Person.class);
log.info("requestParam postForObject result:{}", person);
ResponseEntity<Person> responseEntity = REST_TEMPLATE.postForEntity(url, httpEntity, Person.class);
log.info("requestParam postForEntity result{}", responseEntity);
高级用法
接口返回泛型
第三方接口,返回Person列表
@GetMapping("/listAll")
public List<Person> list() {
return new ArrayList<>(persons.values());
}
请求示例
String url = String.format(URL_FORMAT, "listAll");
// 指定参数类型,这个里面会实际存储返回的类型
ParameterizedTypeReference<List<Person>> parameterizedTypeReference = new ParameterizedTypeReference<List<Person>>() {
};
ResponseEntity<List<Person>> exchange = REST_TEMPLATE.exchange(url, HttpMethod.GET, null, parameterizedTypeReference);
log.info("testGeneric result:{}", exchange);
List<Person> personList = exchange.getBody();
log.info("person List:{}", personList);
定制化RestTemplate
这里主要是对RestTemplate的一些属性进行设置,比如设置RestTemplate使用的http连接池信息,示例代码如下
private static HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 开始设置连接池
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
new PoolingHttpClientConnectionManager();
// 最大连接数
poolingHttpClientConnectionManager.setMaxTotal(100);
// 同路由并发数
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(10);
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
// 不进行重试
httpClientBuilder.setRetryHandler(new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if (exception instanceof org.apache.http.NoHttpResponseException) {
// 此类情况需要进行重试
log.warn("No response from server on " + executionCount + " call");
return true;
}
return false;
}
});
HttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
// httpClient连接配置
new HttpComponentsClientHttpRequestFactory(httpClient);
// 连接超时
clientHttpRequestFactory.setConnectTimeout(5000);
// 数据读取超时时间
clientHttpRequestFactory.setReadTimeout(30000);
// 连接不够用的等待时间
clientHttpRequestFactory.setConnectionRequestTimeout(5000);
return clientHttpRequestFactory;
} catch (Exception e) {
log.error("初始化HTTP连接池出错", e);
}
return null;
}
设置RestTemplate
new RestTemplateBuilder().requestFactory(() -> clientHttpRequestFactory()).build();
最后
RestTemplate的功能非常强大,本文主要介绍了一些常用的功能,以及之前踩过的坑,希望可以帮助到大家。
网友评论