文章目录
常用http请求库
- HttpClient:apache旗下项目,代码较复杂和冗余,且需自己处理资源回收,不推荐直接使用。
- OKhttp:square公司开源的java版本http客户端工具。
- retrofit:square公司开源的基于okhttp进一步封装的retrofit工具,可以把它理解为OKhttp的加强版。用来支持通过接口的方式发起http请求。
- RestTemplate:Spring提供的用于访问Rest服务的客户端。RestTemplate提供了许多便捷访问远程Http服务的方法,可以较好的提高编码效率。
使用HttpClient或者RestTemplate时,一般都会自定义封装一个HttpUtil来使用。
1. HttpClient
Apache HttpClient的两个版本:
- 3.x版本:项目名称:The Commons HttpClient。后来独立出去了,现在这个项目已经结束了它的生命周期,不再开发和维护。
- 4.x后的版本:项目名称:Apache Httpcomponents。项目包括HttpClient和HttpCore两大模块(HttpCore:HTTP协议实现包;HttpClient:基于HttpCore的一套客户端)。
使用方法
- 创建HttpClient
- 创建http请求,如HttpGet、HttpPost
- 添加请求参数
- 添加请求设置,如超时等
- 使用HttpClient执行http请求
- 读取返回内容并释放连接
使用示例
环境依赖
spring-boot-2.2.2.RELEASE
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
spring-boot-2.2.2.RELEASE默认httpclient版本4.5.10。
配置类
/**
* @author: lijiangang
* @date: 2022-4-2 14:50
*/
@Configuration
public class HttpClientConfig {
@Bean
public CloseableHttpClient httpClient() {
// 长连接保持30秒
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
//设置整个连接池最大连接数 根据自己的场景决定
connectionManager.setMaxTotal(500);
//同路由的并发数,路由是对maxTotal的细分
connectionManager.setDefaultMaxPerRoute(500);
RequestConfig requestConfig = RequestConfig.custom()
//服务器返回数据(response)的时间,超过该时间抛出read timeout
.setSocketTimeout(10000)
//连接上服务器(握手成功)的时间,超出该时间抛出connect timeout
.setConnectTimeout(5000)
//从连接池中获取连接的超时时间,超过该时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
.setConnectionRequestTimeout(500)
.build();
//headers
List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
headers.add(new BasicHeader("Accept-Language", "zh-CN"));
headers.add(new BasicHeader("Connection", "Keep-Alive"));
headers.add(new BasicHeader("Content-type", "application/json;charset=UTF-8"));
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.setDefaultHeaders(headers)
// 保持长连接配置,需要在头添加Keep-Alive
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
//重试次数,默认是3次,没有开启
// .setRetryHandler(new DefaultHttpRequestRetryHandler(2, true))
.build();
}
}
自定义请求工具类
@Component
public class HttpClientUtil {
@Autowired
private CloseableHttpClient httpClient;
public String get(String url) {
CloseableHttpResponse response = null;
BufferedReader in;
String result = "";
try {
// 创建http请求,请求设置,请求参数
HttpGet httpGet = new HttpGet(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpGet.setConfig(requestConfig);
httpGet.addHeader("Content-type", "application/json; charset=utf-8");
httpGet.setHeader("Accept", "application/json");
// 执行http请求
response = httpClient.execute(httpGet);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer sb = new StringBuffer();
String line;
// 获取操作系统对应的换行符
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
result = sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != response) {
// 释放连接
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public String post(String url, Object param) {
CloseableHttpResponse response = null;
BufferedReader in;
String result = "";
try {
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpPost.setConfig(requestConfig);
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setHeader("Accept", "application/json");
httpPost.setEntity(new StringEntity(JSON.toJSONString(param), Charset.forName("UTF-8")));
response = httpClient.execute(httpPost);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer sb = new StringBuffer();
String line;
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
result = sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != response) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
使用
@RestController
@RequestMapping("/http")
public class HttpTestController {
@Autowired
private HttpClientUtil httpClientUtil;
@GetMapping("test")
public ApiResult test() {
return ApiUtil.success();
}
@GetMapping("test1")
public ApiResult test1() {
String json = httpClientUtil.get("http://127.0.0.1:8888/http/test");
return JSON.parseObject(json, new TypeReference<ApiResult>() {});
}
}
详细使用可参考:https://www.cnblogs.com/qnlcy/p/15378446.html
2. OKhttp
OkHttp是一个高效的HTTP客户端,允许所有同一个主机地址的请求共享同一个socket连接;连接池减少请求延时;透明的GZIP压缩减少响应数据的大小;缓存响应内容,避免一些完全重复的请求。
当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0。
使用方法
配置OkHttpClient。
使用示例
依赖配置
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.10.0</version>
</dependency>
配置类
@Configuration
public class OkHttpConfig {
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
// .sslSocketFactory(sslSocketFactory(), x509TrustManager())
.retryOnConnectionFailure(false)
.connectionPool(pool())
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
}
@Bean
public X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
@Bean
public SSLSocketFactory sslSocketFactory() {
try {
//信任任何链接
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return null;
}
@Bean
public ConnectionPool pool() {
return new ConnectionPool(200, 5, TimeUnit.MINUTES);
}
}
使用
@RestController
@RequestMapping("/http")
public class HttpTestController {
@Autowired
private OkHttpUtil okHttpUtil;
@GetMapping("test")
public ApiResult test() {
return ApiUtil.success();
}
@GetMapping("test2")
public ApiResult test2() {
String json = okHttpUtil.get("http://127.0.0.1:8888/http/test", null);
return JSON.parseObject(json, new TypeReference<ApiResult>() {});
}
}
3. Retrofit
retrofit是现在比较流行的网络请求框架,可以理解为okhttp的加强版,底层封装了okhttp。准确来说,Retrofit是一个RESTful的http网络请求框架的封装。因为网络请求工作本质上是由okhttp来完成,而Retrofit负责网络请求接口的封装。
底层原理
应用程序通过Retrofit请求网络,实质上是使用Retrofit接口层封装请求参数、Header、Url等信息,之后由okhttp来完成后续的请求工作。在服务端返回数据后,okhttp将原始数据交给Retrofit,Retrofit根据用户需求解析。
Retrofit将okhttp请求抽象成java接口,采用注解描述和配置网络请求参数,用动态代理将该接口的注解“翻译”成一个Http请求,最后执行Http请求。
注意:接口中的每个方法的参数都要用注解标记,否则会报错。
Retrofit的优点
- 超级解耦 ,接口定义、接口参数、接口回调不在耦合在一起
- 可以配置不同的httpClient来实现网络请求,如okhttp、httpclient
- 支持同步、异步、Rxjava
- 可以配置不同反序列化工具类来解析不同的数据,如json、xml
- 请求速度快,使用方便灵活简洁
相关注解
1. 请求方法
注解 | 描述 |
---|---|
@GET | get请求 |
@POST | post请求 |
@PUT | put请求 |
@DELETE | delete请求 |
@PATCH | patch请求,该请求是对put请求的补充,用于更新局部资源 |
@HEAD | head请求 |
@OPTIONS | options请求 |
@HTTP | 通过注解,可以替换以上所有的注解,它拥有三个属性:method、path、hasBody |
2. 请求头
注解 | 描述 |
---|---|
@Headers | 用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在 |
@Header | 用于添加不固定的header,作为方法的参数传入,它会更新已有请求头 |
3. 请求参数
注解 | 描述 |
---|---|
@Body | 多用于Post请求发送非表单数据,根据转换方式将实例对象转化为对应字符串传递参数,比如使用Post发送Json数据,添加GsonConverterFactory则是将body转化为json字符串进行传递 |
@Filed | 多用于Post方式传递参数,需要结合@FromUrlEncoded使用,即以表单的形式传递参数 |
@FiledMap | 多用于Post请求中的表单字段,需要结合@FromUrlEncoded使用 |
@Part | 用于表单字段,Part和PartMap与@multipart注解结合使用,适合文件上传的情况 |
@PartMap | 用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传 |
@Path | 用于Url中的占位符 |
@Query | 用于Get请求中的参数 |
@QueryMap | 与Query类似,用于不确定表单参数 |
@Url | 指定请求路径 |
4. 请求和响应格式(标记)
注解 | 描述 |
---|---|
@FromUrlCoded | 表示请求发送编码表单数据,每个键值对需要使用@Filed注解 |
@Multipart | 表示请求发送form_encoded数据(使用于有文件上传的场景),每个键值对需要用@Part来注解键名,随后的对象需要提供值 |
@Streaming | 表示响应用字节流的形式返回,如果没有使用注解,默认会把数据全部载入到内存中,该注解在下载大文件时特别有用 |
使用方法
自定义请求接口,采用注解描述和配置请求参数。
使用示例
本示例不是直接使用的retrofit依赖,而是使用的retrofit整合springboot依赖。
retrofit依赖
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.9.0</version>
</dependency>
retrofit整合springboot依赖
<dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
启动类配置
@SpringBootApplication
@RetrofitScan("com.joker.http.retrofit")
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
自定义接口
import com.github.lianjiatech.retrofit.spring.boot.annotation.RetrofitClient;
import com.joker.common.base.result.ApiResult;
import com.joker.pojo.User;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.http.*;
import java.util.Map;
@RetrofitClient(baseUrl = "http://127.0.0.1:8888")
public interface RetrofitHttpApi {
@GET("http/test")
ApiResult test();
/**
* 简单的GET请求
* @Query:用于Get请求中的参数
*/
@GET("http/test1")
ApiResult test1(@Query("id") long id);
/**
* 不确定参数的GET请求
* @QueryMap 与@Query类似,用于不确定表单参数。通过Map将不确定的参数存入,然后在方法体中带给网络接口。
*/
@GET("http/test2")
ApiResult test2(@QueryMap Map<String, Object> map);
/**
* From表单POST请求
* @FormUrlEncoded:请求格式注解,表明请求实体是一个From表单,每个键值对需要使用@Field注解
* @Field:表单参数,而且需要配合@FormUrlEncoded使用
* @return
*/
@FormUrlEncoded
@POST("http/test3")
ApiResult test3(@Field("name") String name, @Field("age") Integer age);
/**
* 不确定参数的From表单POST请求
*/
@FormUrlEncoded
@POST("http/test4")
ApiResult test4(@FieldMap Map<String, Object> map);
/**
* json方式POST请求
*/
@POST("http/test5")
ApiResult test5(@Body User user);
/**
* 文件上传表单POST请求
* @Multipart:表示请求实体是一个支持文件上传的表单,需要配合@Part和@PartMap使用,适用于文件上传
* @Part:用于表单字段,适用于文件上传的情况,@Part支持三种类型:RequestBody、MultipartBody.Part任意类型
* @PartMap:用于多文件上传, 与@FieldMap和@QueryMap的使用类似
* @return
*/
@Multipart
@POST("http/test6")
ApiResult test6(@Part("name") RequestBody name, @Part MultipartBody.Part file);
/**
* 多文件上传POST请求
* @param map
* @return
*/
@Multipart
@POST("http/test7")
ApiResult test7(@PartMap Map<String, MultipartBody.Part> map);
/**
* 路径参数请求
* @param id
* @return
*/
@GET("http/test8/{id}")
ApiResult test8(@Path("id") Long id);
/**
* 请求头参数请求
* @Header:请求头注解,用于添加不固定请求头
* @param token
* @return
*/
@GET("http/test9")
ApiResult test9(@Header("token") String token);
/**
* 请求头参数请求
* @Headers:请求头注解,用于添加固定请求头,可以添加多个
* @return
*/
@Headers({"version:1.0","os:windows"})
@GET("http/test10")
ApiResult test10();
/**
* 动态url请求
* @Url:将地址以参数的形式传入,请求方法注解中定义的url忽略
* @param url
* @return
*/
@GET("http/test11")
ApiResult test11(@Url String url);
/**
* 下载文件请求
* @Streaming:响应体的数据用流的方式返回,使用于返回数据比较大,该注解在下载大文件时特别有用
* @return
*/
@Streaming
@POST("http/test12")
ResponseBody test12();
/**
* @HTTP:注解的作用是替换@GET、@POST、@PUT、@DELETE、@HEAD以及更多拓展功能
* @return
*/
@HTTP(method = "GET", path = "http/test13", hasBody = false)
ApiResult test13();
}
详细使用可参考:https://blog.csdn.net/why_still_confused/article/details/108041657
4. RestTemplate
RestTemplate是Spring提供的用于访问Rest服务的客户端库。它提供了一套接口,实现这套接口的http库都可以通过RestTemplate来使用。
常用的有:
- JDK自带的HttpURLConnection
- Apache的HttpClient
- OKHttp
RestTemplate默认使用的http库是JDK自带的HttpURLConnection来建立HTTP连接(使用SimpleClientHttpRequestFactory客户端)。另外三方http库通过实现ClientHttpRequestFactory接口来自定义客户端,可以通过RestTemplate的setRequestFactory指定要使用的客户端。
ClientHttpRequestFactory接口的实现类:
- SimpleClientHttpRequestFactory(Spring默认使用的客户端)
- HttpComponentsClientHttpRequestFactory(Apache HttpClient使用的客户端)
- OkHttp3ClientHttpRequestFactory(OKHttp使用的客户端)
RestTemplate在spring-web包中。Spring项目有spring-web包即可,SpringBoot项目有spring-boot-starter-web包即可,如果要少使用Apache的HttpClient、OKHttp等相关三方http库,则还需要器对应的包。
RestTemplate内部模板使用HttpMessageConverter实例将HTTP消息转换为POJO和从POJO转换。主要MIME类型的转换器是默认注册的,也可以通过setMessageConverters方法注册其他转换器。
RestTemplate提供操作http的方法
方法名称 | 支持的HTTP方法 | 方法描述 |
---|---|---|
getForObject | GET | 直接返回响应对象,可设置路径参数 |
getForEntity | GET | 返回封装的响应对象,可设置路径参数 |
postForObject | POST | 直接返回响应对象,可设置路径参数,可设置请求body和请求header |
postForEntity | POST | 返回封装的响应对象,可设置路径参数,可设置请求body和请求header |
postForLocation | POST | 返回URI对象,适用于返回网络资源的请求方式。可设置路径参数,可设置请求body和请求header |
put | PUT | 无返回值,可设置路径参数,可设置请求body和请求header |
delete | DELETE | 无返回值,可设置路径参数 |
headForHeaders | HEAD | 返回HttpHeaders,可设置路径参数 |
optionsForAllow | OPTIONS | 返回Set<HttpMethod>,可设置路径参数 |
execute | 都支持 | 直接返回响应对象,通用请求方法 |
exchange | 都支持 | 返回封装的响应对象,通用请求方法 |
注意:封装的响应对象除了能获取到响应对象,还能额外获取到响应状态,响应Header信息。
使用方法
配置RestTemplate(如果不配置客户端,会使用默认的客户端SimpleClientHttpRequestFactory),就可以直接使用。
方法1
使用spring默认客户端SimpleClientHttpRequestFactory
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
方法2
使用Apache HttpClient客户端(HttpComponentsClientHttpRequestFactory)。
@Bean
@DependsOn("httpClient")
public RestTemplate restTemplate3(HttpClient httpClient) {
RestTemplate restTemplate = new RestTemplate(httpComponentsClientHttpRequestFactory(httpClient));
setRestTemplate(restTemplate);
return restTemplate;
}
@Bean
public ClientHttpRequestFactory httpComponentsClientHttpRequestFactory(HttpClient httpClient) {
return new HttpComponentsClientHttpRequestFactory();
}
HttpClient的配置参考上面HttpClient的使用示例。
方法3
使用OKHttp客户端(OkHttp3ClientHttpRequestFactory)。
@Bean
@DependsOn("okHttpClient")
public RestTemplate restTemplate4(OkHttpClient okHttpClient) {
RestTemplate restTemplate = new RestTemplate(okHttp3ClientHttpRequestFactory(okHttpClient));
setRestTemplate(restTemplate);
return restTemplate;
}
@Bean
public ClientHttpRequestFactory okHttp3ClientHttpRequestFactory(OkHttpClient okHttpClient) {
return new OkHttp3ClientHttpRequestFactory(okHttpClient);
}
OkHttpClient的配置参考上面OKhttp的使用示例。
使用示例
1.先按上面的使用方法配置好RestTemplate。
2.进行接口测试,示例如下:
@Autowired
private RestTemplate restTemplate;
@GetMapping("test4")
public ApiResult test4() {
// 1.Get请求
ApiResult result = restTemplate.getForObject(getUrl("get"), ApiResult.class);
System.out.println("Get请求:" + JSON.toJSONString(result));
// 2.设置路径参数的Get请求
result = restTemplate.getForObject(getUrl("get/{id}"), ApiResult.class, 123);
System.out.println("设置路径参数的Get请求:" + JSON.toJSONString(result));
// 3.封装返回对象的Get请求
ResponseEntity<ApiResult> responseEntity = restTemplate.getForEntity(getUrl("get"), ApiResult.class);
System.out.println("封装返回对象的Get请求:" + responseEntity.getBody());
// 4.设置header的Get请求
HttpHeaders headers = new HttpHeaders();
headers.add("token", "token");
ResponseEntity<ApiResult> response = restTemplate.exchange(getUrl("get"), HttpMethod.GET, new HttpEntity<String>(headers), ApiResult.class);
System.out.println("设置header的Get请求:" + response.getBody());
// 5.Post请求
User user = new User();
user.setId("1");
result = restTemplate.postForObject(getUrl("post"), user, ApiResult.class);
System.out.println("Post请求:" + result);
// 6.设置header的Post请求
response = restTemplate.postForEntity(getUrl("post"), new HttpEntity<>(user, headers), ApiResult.class);
System.out.println("设置header的Post请求:" + response.getBody());
// 7.设置header的put请求
// 无返回值
restTemplate.put(getUrl("put"), new HttpEntity<>(user, headers));
// 带返回值
response = restTemplate.exchange(getUrl("put"), HttpMethod.PUT, new HttpEntity<>(user, headers), ApiResult.class);
System.out.println("设置header的put请求:" + response.getBody());
// 8.设置路径参数的del请求
// 无返回值
restTemplate.delete(getUrl("delete/{id}"), 123);
// 带返回值
response = restTemplate.exchange(getUrl("delete/{id}"), HttpMethod.DELETE, null, ApiResult.class,123);
System.out.println("设置路径参数的del请求:" + response.getBody());
return ApiUtil.success();
}