Skip to content

czarea/rest-tool

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

源码地址:rest-tool

  • springboot2.1.4
  • java11
  • gradle5.4.1

简介

java中http请求封装历史

RestTemplate.png

Spring提供的RestTemplate是基于HttpClient等基础上封装,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接,我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。

RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以替换为例如 Apache HttpComponents、OkHttp或者Netty等其它HTTP library。默认SpringBoot封装了HttpClient,OkHttp,只用把HttpClient或者OkHttp加入到依赖jar即可。

implementation("org.apache.httpcomponents:httpclient:4.5.8") implementation("com.squareup.okhttp3:okhttp:3.14.2") 

实现逻辑

RestTemplate包含以下几个部分:

  • UriTemplateHandler 地址模版转换器 默认使用@PathVariable("xx")方式
  • RequestFactory
  • HttpMessageConverter 对象转换器
  • ClientHttpRequestFactory 默认是JDK的HttpURLConnection
  • ResponseErrorHandler 异常处理
  • ClientHttpRequestInterceptor 请求拦截器

一般使用RestTemplateBuilder构建RestTemplate。

@bean public RestTemplateBuilder restTemplateBuilder(MyResponseErrorHandler myResponseErrorHandler,HttpClientRequestFactory httpClientRequestFactory) { HttpMessageConverters converters = this.messageConverters.getIfUnique(); return new RestTemplateBuilder() .rootUri("http://localhost:8080") .uriTemplateHandler(new MyUriTemplateHandler()) .errorHandler(myResponseErrorHandler) .requestFactory(httpClientRequestFactory) .messageConverters(converters.getConverters()) .additionalInterceptors(new RestHttpRequestInterceptor()); } 

实现业务逻辑

rest_logic.png

简单使用

GET

@Test public void testSGet() { Foo foo = restTemplate.getForEntity(rootUrl + "/foos/s/1", Foo.class).getBody(); Assert.assertEquals(foo.getName(), "name1"); } 

POST

@Test public void testSave() { Foo foo = new Foo(1000L, "foo"); Response response = restTemplate.postForEntity(rootUrl + "/foos", foo, Response.class).getBody(); Assert.assertEquals(response.getCode(), 200); } 

泛型结果请求

泛型使用:ParameterizedTypeReference

@Test public void testList() { Foo foo = new Foo(1L, "name1"); Page page = new Page(0, 10, foo); String url = uriTemplateHandler.expand(rootUrl + "/foos", page).toString(); Response<Grid<Foo>> response = restTemplateService .request(url, HttpMethod.GET, null, new ParameterizedTypeReference<Response<Grid<Foo>>>() { }); Assert.assertEquals(100, response.getData().getTotal()); } 

文件上传

@Test public void testUploadByResource() { LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>(); map.add("file", new ClassPathResource("test.txt")); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers); ResponseEntity<Response> result = restTemplate.exchange(rootUrl + "/foos/upload", HttpMethod.POST, requestEntity, Response.class); Assert.assertEquals(result.getBody().getCode(), 200); } 

文件下载

@Test public void testDownload() throws UnsupportedEncodingException { // 小文件 RequestEntity requestEntity = RequestEntity.get(URI.create(rootUrl + "/foos/download")).build(); ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class); byte[] downloadContent = responseEntity.getBody(); String str = new String(downloadContent, "UTF-8"); // 大文件 ResponseExtractor<ResponseEntity<File>> responseExtractor = response -> { File rcvFile = File.createTempFile("rcvFile", "zip"); FileCopyUtils.copy(response.getBody(), new FileOutputStream(rcvFile)); return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(rcvFile); }; ResponseEntity<File> getFile = this.restTemplate .execute(rootUrl + "/foos/download", HttpMethod.GET, null, responseExtractor); } 

设置自定义异常处理

public class MyResponseErrorHandler implements ResponseErrorHandler { private final Set<Integer> acceptableStatus; public MyResponseErrorHandler(HttpClientProperties httpClientProperties) { this.acceptableStatus = httpClientProperties.getAcceptableStatus(); } @Override public boolean hasError(ClientHttpResponse response) throws IOException { return !acceptableStatus.contains(response.getRawStatusCode()); } @Override public void handleError(ClientHttpResponse response) throws IOException { log.error("Response error: {} {}", response.getStatusCode(), response.getStatusText()); StringBuilder inputStringBuilder = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8")); String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); line = bufferedReader.readLine(); } if (log.isDebugEnabled()) { log.debug("============================response begin=========================================="); log.debug("Status code : {}", response.getStatusCode()); log.debug("Status text : {}", response.getStatusText()); log.debug("Headers : {}", response.getHeaders()); log.debug("Response body: {}", inputStringBuilder.toString()); log.debug("=======================response end================================================="); } throw new MyException(inputStringBuilder.toString()); } } 

设置拦截器

public class RestHttpRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { logRequestDetails(request); return execution.execute(request, body); } private void logRequestDetails(HttpRequest request) { log.info("Headers: {}", request.getHeaders()); log.info("Request Method: {}", request.getMethod()); log.info("Request URI: {}", request.getURI()); } } 

封装扩展

GET请求替换为query params方法

public class MyUriTemplateHandler implements UriTemplateHandler { @Autowired private ObjectMapper mapper; @Override public URI expand(String uri, Map<String, ?> params) { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(uri); for (Map.Entry<String, ?> entry : params.entrySet()) { String firstKey = entry.getKey(); if (entry.getValue() instanceof Map) { Map values = (Map) entry.getValue(); values.forEach((k, v) -> { if (v != null) { uriComponentsBuilder.queryParam(firstKey + "." + k, v); } }); } else { if (entry.getValue() != null) { uriComponentsBuilder.queryParam(firstKey, entry.getValue()); } } } uri = uriComponentsBuilder.build().toUriString(); return URI.create(uri); } @Override public URI expand(String uri, Object... params) { if (params.length != 0 && params[0] != null) { Map map = mapper.convertValue(params[0], Map.class); return expand(uri, map); } else { return URI.create(uri); } } } 

替换FastJson

这个比较简单,注册FastJson,加入到MessageConverters列中,但是涉及到上传和下载的时候还是不要使用FastJson的比较好,会有各种坑。这里是简单的解决办法。继承FastJsonHttpMessageConverter覆盖supports方法,只解析自己想要的包下的类。或者设置fastjson解析的MediaType,只支持JSON解析。

 FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); List<MediaType> supportedMediaTypes = new ArrayList<>(); supportedMediaTypes.add(MediaType.APPLICATION_JSON); converter.setSupportedMediaTypes(supportedMediaTypes); 
public class MyFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter { @Override protected boolean supports(Class<?> clazz) { if (clazz == null) { return true; } if (clazz.getPackageName().contains("com.czarea.rest")) { return true; } else { return false; } } } 

About

RestTemplate

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages