💻目录
- 1、介绍
- 2、使用
- 2.1、添加配置
- 2.1.1、依赖
- 2.1.2、工具类
- 2.1.3、实体
- 2.1.4、Controller接口
- 2.2、Apache HttpClient使用
- 2.3 、OkHttp使用
- 2.4、WebClient使用
1、介绍
现在java使用的http客户端主要包括以下几种
而这些中使用得最频繁的主要是:
-
Apache HttpClient:这是一个功能强大且广泛使用的第三方库,用于进行HTTP通讯。它提供了更高级的API和更丰富的功能,比如支持连接池、认证、重定向、Cookie管理等。Apache HttpClient可以作为独立的库使用,也可以作为Apache HttpComponents项目的一部分。
-
OkHttp:这是另一个流行的第三方库,用于进行HTTP通讯。OkHttp提供了简洁的API和高性能的特性,支持同步和异步请求,以及连接池、缓存、拦截器等功能。OkHttp也是Square公司的一个开源项目。
-
Spring WebClient:这是Spring框架中的一个模块,是RestTemplate的升级版,用于进行非阻塞的HTTP通讯。它基于Reactive Streams编程模型,适用于构建响应式的应用程序。Spring WebClient提供了简单的API来发送HTTP请求和处理响应,可以与Spring WebFlux等模块一起使用。
2、使用
下面展示了Apache HttpClient和OkHttp以及Spring WebClient的常用使用方式,包括get和post
2.1、添加配置
2.1.1、依赖
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<!--okhttp-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<!--webClient-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.1.2、工具类
- Result:统一返回类
@Data
public class Result<T> {
//状态码
private Integer code;
//信息
private String message;
//数据
private T data;
private Result(){}
//设置数据,返回对象的方法
public static <T> Result<T> build(T data, ResultCodeEnum resultCodeEnum) {
//创建Result对象,设置值,返回对象
Result<T> result = new Result<>();
//判断返回结果中是否需要数据
if (data != null) {
//设置数据到result对象
result.setData(data);
}
//设置其他值
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
//返回设置值之后的对象
return result;
}
//成功的方法
public static <T> Result<T> ok(T data) {
return build(data, ResultCodeEnum.SUCCESS);
}
//成功的方法
public static <T> Result<T> ok() {
return build(null, ResultCodeEnum.SUCCESS);
}
//失败的方法
public static <T> Result<T> fail(T data) {
return build(data, ResultCodeEnum.FAIL);
}
}
- ResultCodeEnum:返回结果
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
;
private Integer code;
private String message;
ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
2.1.3、实体
- User
@Data
public class User {
private String name;
private Integer age;
private Integer post;
}
- ProductInfo:返回数据
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class ProductInfo implements Serializable {
private Long id;
/**销量*/
private Integer sale;
/**价格*/
private Integer price;
/**名称*/
private String name;
/**类型*/
private Integer type;
/**端口*/
private Integer port;
}
2.1.4、Controller接口
@RestController
@RequestMapping("/productInfo/index")
public class ProductInfoController {
@Resource
private ProductInfoService productInfoService;
// 获取当前服务的端口
@Value("${server.port}")
private Integer port;
@GetMapping("/get")
public Result<?> get(Integer type){
List<ProductInfo> productInfoList = getProductInfoList(type);
System.out.println(productInfoList);
return Result.ok(productInfoList);
}
@PostMapping("/post")
public Result<?> post(@RequestBody User user){
Integer post = user.getPost();
List<ProductInfo> productInfoList = getProductInfoList(post);
System.out.println(productInfoList);
return Result.ok(productInfoList);
}
public List<ProductInfo> getProductInfoList(Integer type) {
ProductInfo productInfo3 = new ProductInfo(3l,800,20,"哈密瓜1",1,port);
ProductInfo productInfo1 = new ProductInfo(1l,50,8,"苹果1",1,port);
ProductInfo productInfo2 = new ProductInfo(2l,200,13,"牛肉1",2,port);
ProductInfo productInfo4 = new ProductInfo(4l,50,9,"青菜1",2,port);
/* 实际项目中,会从数据库查出数据 */
List<ProductInfo> productInfoList = Arrays.asList(productInfo1,productInfo2,productInfo3,productInfo4);
// 根据传入的类型返回指定类型
List<ProductInfo> result = productInfoList.stream()
.filter(productInfo -> productInfo.getType() == type)
.collect(Collectors.toList());
return result;
}
}
2.2、Apache HttpClient使用
- get使用
默认只能同步异步需要加额外的依赖,get请求不能携带参数,只能通过拼接路径
<!--异步请求-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
</dependency>
public static void get(CloseableHttpClient httpClient,String url){
// 连接对象
try {
// HttpGet不能携带参数,如果需要参数只能通过拼接
HttpGet httpGet = new HttpGet(url+"?type=1");
String execute = httpClient.execute(httpGet, response -> {
JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
Result result = JSONUtil.toBean(entries, Result.class);
if (result.getCode() == 200) {
String data = result.getData().toString();
System.out.println(data);
return data;
} else {
System.err.println(result.getMessage());
return result.getMessage();
}
});
System.out.println("get成功结果:"+execute);
} catch (Exception e) {
e.printStackTrace();
}
}
- post
post请求,通过setEntity()给HttpPost对象添加请求对象
public static void post(CloseableHttpClient httpClient,String url){
HttpPost httpPost = new HttpPost(url);
User user = new User();
user.setPost(1);
// 添加参数对象
httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(user)));
// 设置请求头
httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
try {
String execute = httpClient.execute(httpPost, response -> {
JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
Result result = JSONUtil.toBean(entries, Result.class);
if (result.getCode() == 200) {
String data = result.getData().toString();
log.info(data);
return data;
} else {
log.error(result.getMessage());
return result.getMessage();
}
});
log.info("HttpClient的post成功结果:"+execute);
} catch (IOException e) {
e.printStackTrace();
}
}
- 全部代码
执行步骤:
- 创建CloseableHttpClient对象
- 创建get获取post请求对象
- 通过execute()方法发送请求
- 通过response.getEntity()获取响应回来的数据
@Slf4j
public class HttpClientUtil {
private static String postUrl = "http://localhost:8202/productInfo/index/post";
private static String getUrl = "http://localhost:8202/productInfo/index/get";
public static void main(String[] args) {
try {
// 创建客户端对象
CloseableHttpClient client = HttpClients.createDefault();
get(client,getUrl);
post(client,postUrl);
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void get(CloseableHttpClient httpClient,String url){
// 连接对象
try {
// HttpGet不能携带参数,如果需要参数只能通过拼接
HttpGet httpGet = new HttpGet(url+"?type=1");
String execute = httpClient.execute(httpGet, response -> {
JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
Result result = JSONUtil.toBean(entries, Result.class);
if (result.getCode() == 200) {
String data = result.getData().toString();
log.info(data);
return data;
} else {
log.info(result.getMessage());
return result.getMessage();
}
});
log.info("HttpClient的get成功结果:"+execute);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void post(CloseableHttpClient httpClient,String url){
HttpPost httpPost = new HttpPost(url);
User user = new User();
user.setPost(1);
// 添加参数对象
httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(user)));
// 设置请求头
httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
try {
String execute = httpClient.execute(httpPost, response -> {
JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
Result result = JSONUtil.toBean(entries, Result.class);
if (result.getCode() == 200) {
String data = result.getData().toString();
log.info(data);
return data;
} else {
log.error(result.getMessage());
return result.getMessage();
}
});
log.info("HttpClient的post成功结果:"+execute);
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.3 、OkHttp使用
执行步骤:
- 创建OkHttpClient对象,用于配置和管理HTTP请求的行为
- 创建Request对象,用于描述要发送的HTTP请求。Request对象包含了URL、请求方法、请求头、请求体等信息。
- 创建Call对象,使用OkHttpClient的newCall()方法,传入Request对象,创建一个Call对象,Call对象表示一次HTTP请求的调用,可以用于执行同步或异步的请求。
- 执行请求,同步通过Call对象的execute()方法来;异步通过Call对象的enqueue()方法来执行请求,并通过回调函数处理响应
- 解析响应,通过response.body()得到响应的内容在通过json解析。
@Slf4j
public class OkHttpUtil {
private static String postUrl = "http://localhost:8202/productInfo/index/post";
private static String getUrl = "http://localhost:8202/productInfo/index/get";
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(30, TimeUnit.SECONDS) //连接超时时间
.writeTimeout(30,TimeUnit.SECONDS) //请求超时时间
.readTimeout(30,TimeUnit.SECONDS) //响应超时时间
.build();
// get(client);
// asyncGet(client);
// post(client);
asyncPost(client);
}
/**
* 同步get
* @param client
* @return void
*/
public static void get(OkHttpClient client){
// 创建get请求对象
Request request = new Request.Builder()
.url(getUrl+"?type=1")
.get()
.header("Content-Type", "application/json") // 设置Content-Type请求头
.build();
try {
// 返回响应对象
Response response = client.newCall(request).execute();
// 验证响应是否成功
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// 解码对象
Result result = JSONUtil.toBean(response.body().string(), Result.class);
Object data = result.getData();
System.out.println(data);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 异步get
* @param client
* @return void
*/
public static void asyncGet(OkHttpClient client){
// 创建get请求对象
Request request = new Request.Builder()
.url(getUrl+"?type=1")
.get() //不指定请求方式默认是get
.build();
// 异步发送请求,没有返回结果
client.newCall(request).enqueue(new Callback() {
// 处理失败请求
@Override
public void onFailure(Call call, IOException e) {
log.debug("Unexpected code " + e.getMessage());
e.printStackTrace();
}
// 处理成功请求
@Override
public void onResponse(Call call, Response response) throws IOException {
// 验证响应是否成功
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// 解码对象
Result result = JSONUtil.toBean(response.body().string(), Result.class);
Object data = result.getData();
System.out.println(data);
}
});
}
/**
* 同步post
* @param client
* @return void
*/
public static void post(OkHttpClient client){
User user = new User();
user.setPost(1);
String str = JSONUtil.toJsonStr(user);
Request request = new Request.Builder()
.url(postUrl)
.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), str))
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
// 验证响应是否成功
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// 解码对象
Result result = JSONUtil.toBean(response.body().string(), Result.class);
Object data = result.getData();
log.info("post请求成功,返回结果:{}",data);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 异步post请求
* @param client
* @return void
*/
public static void asyncPost(OkHttpClient client){
User user = new User();
user.setPost(1);
// 把对象转为json字符串
String str = JSONUtil.toJsonStr(user);
Request request = new Request.Builder()
.url(postUrl)
.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), str))
.build();
Call call = client.newCall(request);
// 异步请求没返回
call.enqueue(new Callback() {
// 请求异常
@Override
public void onFailure(Call call, IOException e) {
log.debug("Unexpected code " + e.getMessage());
e.printStackTrace();
}
// 请求成功
@Override
public void onResponse(Call call, Response response) throws IOException {
// 验证响应是否成功
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// 解码对象
Result result = JSONUtil.toBean(response.body().string(), Result.class);
Object data = result.getData();
log.info("异步post请求成功,返回结果:{}",data);
}
});
}
}
2.4、WebClient使用
执行步骤:
-
创建WebClient对象,推荐使用WebClient.builder()的方式创建,因为可以自定义配置客户端的参数,直接new是只能使用默认的配置。
-
构建请求,直接通过webClient对象,如get()、post()等,构建一个请求(如果没有指定请求方式),默认是get请求。
-
处理响应:使用响应对象(如Mono、Flux等)来处理响应数据。你可以通过操作符(如map()、flatMap()等)对响应进行转换、过滤、合并等操作。
-
订阅响应:使用subscribe()方法来订阅响应流,启动请求并处理响应数据。你可以通过回调函数或操作符链来处理响应数据。
@Component
@Slf4j
public class WebClientUtil {
private static String postUrl = "http://localhost:8202/productInfo/index/post";
private static String getUrl = "http://localhost:8202/productInfo/index/get";
/**
* 同步执行get请求
* @param webClient
* @return void
*/
public void get(WebClient webClient){
Mono<Result> mono = webClient.get()
.uri(getUrl + "?type=2")
.retrieve()
.bodyToMono(Result.class);
// 获取返回结果 block()会进行堵塞,等待返回结果
Result result = mono.block();
if (result.getCode()==200){
log.info("get请求返回结果{}",result.getData());
}
}
/**
* 异步get
* @return void
*/
public void asyncGet(){
HttpClient client = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) //设置连接超时
.doOnConnected(conn -> conn
.addHandler(new ReadTimeoutHandler(10, TimeUnit.SECONDS)) //写入超时
.addHandler(new WriteTimeoutHandler(10))); //读取超时
// 也可以以这种方式创建WebClient可以添加请求头和url以及一些参数;
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(client))
.baseUrl(getUrl)
.defaultHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)")
// .defaultCookie() //添加请求Cookie
.build();
// 获取返回结果
Mono<Result> mono = webClient.get()
.uri( "?type=2")// 请求路径,会拼接上面的
.accept(MediaType.APPLICATION_JSON)
// .retrieve() //获取响应体
// .bodyToMono(Result.class); //指定获取的类型,直接获取结果。
// 或者通过该方式手动处理结果
.exchangeToMono(response->{
if (response.statusCode().equals(HttpStatus.OK)) {
// 成功返回
return response.bodyToMono(Result.class);
}
else {
// 失败返回
return response.createException().flatMap(Mono::error);
}
});
// 异步获取返回结果
mono
.subscribe(result -> {
if (result.getCode() == 200) {
log.info("get请求返回结果{}", result.getData());
}
});
System.out.println("执行完成");
}
/**
* 同步post
* @return void
*/
public void post(){
// 创建 WebClient 对象
WebClient webClient = WebClient.builder()
.baseUrl(getUrl)
.build();
User user = new User();
user.setPost(2);
Mono<Result> mono = webClient
.post()
.uri(postUrl)
.contentType(MediaType.APPLICATION_JSON)
.header("token","12321412")
.body(BodyInserters.fromValue(user))
.retrieve()
.bodyToMono(Result.class);
Result result = mono.block();
if (result.getCode()==200){
log.info("post请求返回结果{}",result.getData());
}
}
/**
* WebClient异步请求
* @return void
*/
public void asyncPost(){
// 创建 WebClient 对象
WebClient webClient = WebClient.builder()
.baseUrl(getUrl)
.build();
User user = new User();
user.setPost(2);
Mono<Result> mono = webClient
.post()
.uri(postUrl)
.contentType(MediaType.APPLICATION_JSON) //指定类型
.header("token","12321412") //添加请求头
.body(BodyInserters.fromValue(user)) //添加请求对象
.retrieve()
.bodyToMono(Result.class);
// 异步请求
mono.subscribe(result -> {
if (result.getCode()==200){
log.info("post异步请求返回结果{}",result.getData());
}
});
}
}