目录
  1. SpringBoot使用RestTemplate发送HTTP请求
    1. 一、简述RestTemplate
    2. 二、get请求实践
      1. 1. getForObject()方法
      2. 2. getForEntity()方法
    3. 三、POST请求实践
      1. 1. postForObject()方法
      2. 2. postForEntity()方法
      3. 3. 事例
      4. 4. 发送 @RequestBody JSONObject 请求
    4. 四、PUT请求实践
    5. 五、DELETE请求实践
    6. 六、使用exchange指定调用方式
    7. 七、excute()指定调用方式
    8. 八、上传文件
SpringBoot使用RestTemplate发送HTTP请求

SpringBoot使用RestTemplate发送HTTP请求

RestTemplateSpring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。我之前的HTTP开发是用apacheHttpClient开发,代码复杂,还得操心资源回收等。代码很复杂,冗余代码多。

本教程将带领大家实现Spring生态内RestTemplateGET请求和POST请求还有exchange指定请求类型的实践和RestTemplate核心方法源码的分析,看完你就会用优雅的方式来发HTTP请求。

RestTemplateSpring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。我之前的HTTP开发是用apacheHttpClient开发,代码复杂,还得操心资源回收等。代码很复杂,冗余代码多。

本教程将带领大家实现Spring生态内RestTemplateGET请求和POST请求还有exchange指定请求类型的实践和RestTemplate核心方法源码的分析,看完你就会用优雅的方式来发HTTP请求。

一、简述RestTemplate

RestTemplateSpring Boot用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。默认情况下,RestTemplate默认依赖jdkHTTP连接工具。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponentsNettyOkHttp

RestTemplate能大幅简化了提交表单数据的难度,并且附带了自动转换JSON数据的功能,但只有理解了HttpEntity的组成结构(header与body),且理解了与uriVariables之间的差异,才能真正掌握其用法。这一点在Post请求更加突出,该类的入口主要是根据HTTP的六个方法制定:

在内部,RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息。默认情况下会注册主mime类型的转换器,但也可以通过setMessageConverters注册其他的转换器。其实这点在使用的时候是察觉不到的,很多方法有一个responseType 参数,它让你传入一个响应体所映射成的对象,然后底层用HttpMessageConverter将其做映射

在内部,RestTemplate默认使用SimpleClientHttpRequestFactory和DefaultResponseErrorHandler来分别处理HTTP的创建和错误,但也可以通过setRequestFactory和setErrorHandler来覆盖。

二、get请求实践

1. getForObject()方法

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables){}
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
public <T> T getForObject(URI url, Class<T> responseType)

getForObject()其实比getForEntity()多包含了将HTTP转成POJO的功能,但是getForObject没有处理response的能力。因为它拿到手的就是成型的pojo。省略了很多response的信息。

@SpringBootTest
class RestTemplateApplicationTests {
/**
* 不带参的get请求
*/
@Test
public void restTemplateGetTest(){
RestTemplate restTemplate = new RestTemplate();
Notice notice = restTemplate.getForObject("http://xxx.top/notice/list/1/5"
, Notice.class);
System.out.println(notice);
}

/**
* 带参的get请求方式一
*/
@Test
public void restTemplateGetTest(){
RestTemplate restTemplate = new RestTemplate();
Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/{1}/{2}"
, Notice.class, 1, 5);
System.out.println(notice);
}

/**
* 带参的get请求方式二
*/
@Test
public void restTemplateGetTest(){
RestTemplate restTemplate = new RestTemplate();
Map<String,String> map = new HashMap();
map.put("start","1");
map.put("page","5");
Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/{start}/{page}"
, Notice.class, map);
System.out.println(notice);
}
}

2. getForEntity()方法

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables){}
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables){}
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType){}

与getForObject()方法不同的是返回的是ResponseEntity对象,如果需要转换成pojo,还需要json工具类的引入,这个按个人喜好用。

@SpringBootTest
class RestTemplateApplicationTests {
/**
* 不带参的get请求
*/
@Test
public void restTemplateGetTest(){
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Notice> entity = restTemplate.getForEntity("http://xxx.top/notice/list/1/5"
, Notice.class);
System.out.println("statusCode.is2xxSuccessful(): "+entity.getStatusCode().is2xxSuccessful());
System.out.println("entity.getStatusCode(): "+entity.getStatusCode());
System.out.println("entity.getStatusCodeValue(): "+entity.getStatusCodeValue());
System.out.println("entity.getHeaders(): "+entity.getHeaders().toString());
System.out.println("entity.getBody(): "+entity.getBody());
}

/**
* 带参的get请求方式一
*/
@Test
public void restTemplateGetTest(){
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Notice> entity = restTemplate.getForEntity("http://fantj.top/notice/list/{1}/{2}"
, Notice.class, 1, 5);
System.out.println("statusCode.is2xxSuccessful(): "+entity.getStatusCode().is2xxSuccessful());
System.out.println("entity.getStatusCode(): "+entity.getStatusCode());
System.out.println("entity.getStatusCodeValue(): "+entity.getStatusCodeValue());
System.out.println("entity.getHeaders(): "+entity.getHeaders().toString());
System.out.println("entity.getBody(): "+entity.getBody());
}

/**
* 带参的get请求方式二
*/
@Test
public void restTemplateGetTest(){
RestTemplate restTemplate = new RestTemplate();
Map<String,String> map = new HashMap();
map.put("start","1");
map.put("page","5");
ResponseEntity<Notice> entity = restTemplate.getForEntity("http://fantj.top/notice/list/{start}/{page}"
, Notice.class, map);
System.out.println("statusCode.is2xxSuccessful(): "+entity.getStatusCode().is2xxSuccessful());
System.out.println("entity.getStatusCode(): "+entity.getStatusCode());
System.out.println("entity.getStatusCodeValue(): "+entity.getStatusCodeValue());
System.out.println("entity.getHeaders(): "+entity.getHeaders().toString());
System.out.println("entity.getBody(): "+entity.getBody());
}
}

三、POST请求实践

1. postForObject()方法

public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {}
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {}
public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException {}

2. postForEntity()方法

public <T> T postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {}
public <T> T postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {}
public <T> T postForEntity(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException {}

3. 事例

/**
* Simple POST
*/
private static void createEmployee(){
final String uri = "http://localhost:8080/springrestexample/employees";
EmployeeVO newEmployee = new EmployeeVO(-1, "Adam", "Gilly", "test@email.com");
RestTemplate restTemplate = new RestTemplate();
EmployeeVO result = restTemplate.postForObject( uri, newEmployee, EmployeeVO.class);
System.out.println(result);
}
/**
* Submit Form Data
*/
private static void postEmployee(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("id", "1");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
RestTemplate restTemplate = new RestTemplate();
EmployeeVO result = restTemplate.postForObject( uri, request, EmployeeVO.class);
System.out.println(result);
}

4. 发送 @RequestBody JSONObject 请求

/**
* 请求端代码
*/
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("deviceId", "1345");
jsonObject.put("commandCode", "553339299121");
try {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new FastJsonHttpMessageConverter());
restTemplate.postForObject("http://localhost:8080/Lock/master/sendCmd", jsonObject, void.class);
} catch (RestClientException e) {
e.printStackTrace();
}
}

/**
* 服务端代码
*/
@RequestMapping(value = "/sendCmd", method = RequestMethod.POST)
@ResponseBody
public void sendCommnand(@RequestBody JSONObject json){
logger.info(json.toJSONString());
}
  • 如果不需要返回值,两边可以直接配成 void。
  • 两边的参数的结果要相同,否则会报415错误,即不支持的类型。
  • 如果服务端有返回值,但是请求端是用void接收,会报错。(返回值怎么可能转化成void)
  • 如果服务端没有返回值,请求端如果用其他类型接收,都是null。
  • 即便 RestTemplate 中的返回值和服务端的返回值不同,也可以请求到,只是客户端会报异常:org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type ……
  • 服务端的参数要加 @RequestBody 的注解

四、PUT请求实践

/**
* Simple PUT
*/
private static void updateEmployee(){
final String uri = "http://localhost:8080/springrestexample/employees/{id}";

Map<String, String> params = new HashMap<String, String>();
params.put("id", "2");

EmployeeVO updatedEmployee = new EmployeeVO(2, "New Name", "Gilly", "test@email.com");

RestTemplate restTemplate = new RestTemplate();
restTemplate.put ( uri, updatedEmployee, params);
}

/**
* 使用.exchange和请求回调
*/

五、DELETE请求实践

private static void deleteEmployee(){
final String uri = "http://localhost:8080/springrestexample/employees/{id}";

Map<String, String> params = new HashMap<String, String>();
params.put("id", "2");

RestTemplate restTemplate = new RestTemplate();
restTemplate.delete ( uri, params );
}

六、使用exchange指定调用方式

exchange()方法跟上面的getForObject()、getForEntity()、postForObject()、postForEntity()等方法不同之处在于它可以指定请求的HTTP类型。但是你会发现exchange的方法中似乎都有@Nullable HttpEntity requestEntity这个参数,这就意味着我们至少要用HttpEntity来传递这个请求体,之前说过源码所以建议就使用HttpEntity提高性能。

@Test
public void rtExchangeTest() throws JSONException {
RestTemplate restTemplate = new RestTemplate();
String url = "http://xxx.top/notice/list";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
JSONObject jsonObj = new JSONObject();
jsonObj.put("start",1);
jsonObj.put("page",5);

HttpEntity<String> entity = new HttpEntity<>(jsonObj.toString(), headers);
ResponseEntity<JSONObject> exchange = restTemplate.exchange(url,
HttpMethod.GET, entity, JSONObject.class);
System.out.println(exchange.getBody());
}

七、excute()指定调用方式

excute()的用法与exchange()大同小异了,它同样可以指定不同的HttpMethod,不同的是它返回的对象是响应体所映射成的对象,而不是ResponseEntity。需要强调的是,execute()方法是以上所有方法的底层调用。

@Override
@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {

RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}

八、上传文件

public static void transFile(){
String url = "http://localhost:8080/Lock/nbdc/remoteUpdate";
RestTemplate restTemplate = new RestTemplate();
FileSystemResource resource = new FileSystemResource(new File("C:\\Users\\Administrator\\Desktop\\40.txt"));
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
param.add("deviceId", "123424");
param.add("file", resource);
Boolean recv = restTemplate.postForObject(url, param, Boolean.class);
System.out.println(recv);
}
  • 如果有 @RequestParam 注解,MultiValueMap中的名字必须与它对应,否则请求不到。
  • 如果没有 @RequestParam 注解,MultiValueMap中的名字与它不对应,可以请求到,但是file为null。
文章作者: Gadfly
文章链接: https://blog.gadfly.pub/2019/12/23/cheng-xu-she-ji/springboot-shi-yong-resttemplate-fa-song-http-qing-qiu/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 牛虻的世界
打赏
  • 微信
  • 支付寶

评论