1.JDK中的HttpURLConnection
使用JDK 1.8中自带的rt.jar包中的java.net中的HttpURLConnection
public static void postTest() throws Exception{
// 1.请求URL
String postUrl = "";
// 2.请求参数JSON格式
Map<String, String> parammap = new HashMap<>();;
String json = JSON.toJSONString(map);
// 3.创建连接与设置连接参数
URL urlObj = new URL(postUrl);
HttpURLConnection httpConn = (HttpURLConnection) urlObj.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Charset", "UTF-8");
// POST请求且JSON数据,必须设置
httpConn.setRequestProperty("Content-Type", "application/json");
// 打开输出流,默认是false
httpConn.setDoOutput(true);
// 打开输入流,默认是true,可省略
httpConn.setDoInput(true);
// 4.从HttpURLConnection获取输出流和写数据
OutputStream oStream = httpConn.getOutputStream();
oStream.write(json.getBytes());
oStream.flush();
// 5.发起http调用(getInputStream触发http请求)
if (httpConn.getResponseCode() != 200) {
throw new Exception("调用服务端异常.");
}
// 6.从HttpURLConnection获取输入流和读数据
BufferedReader br = new BufferedReader(
new InputStreamReader(httpConn.getInputStream()));
String resultData = br.readLine();
System.out.println("从服务端返回结果: " + resultData);
// 7.关闭HttpURLConnection连接
httpConn.disconnect();
}
2.commons-httpclient中的HttpClient
引入依赖
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
</dependency>
public static void postTest() throws Exception {
// 1.请求URL
String postUrl = "";
// 2.请求参数
Map<String, String> parammap = new HashMap<>();
String json = JSON.toJSONString(parammap);
// 3.创建连接与设置连接参数
HttpClient httpClient = new HttpClient();
PostMethod postMethod = new PostMethod(postUrl);
postMethod.addRequestHeader("Content-Type", "application/json");
RequestEntity entity = new StringRequestEntity(json, "application/json", "UTF-8");
postMethod.setRequestEntity(entity);
//解决返回值中文乱码
postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
String resultData = "";
// 4.发起请求
int code = httpClient.executeMethod(postMethod);
if (code != 200) {
throw new Exception("调用服务端异常.");
}
// 5.接收返回值
resultData = postMethod.getResponseBodyAsString();
System.out.println("从服务端返回结果: " + resultData);
// 6.关闭连接
postMethod.releaseConnection();
}
3.httpclient中的HttpClientBuilder
HttpClient:是apache httpClient包下的,代码复杂,需要资源回收。
引入依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
public static void postTest() throws Exception {
// 1.请求URL
String postUrl = "";
// 2.请求参数
Map<String, String> paramMap = new HashMap<>();
String json = JSON.toJSONString(paramMap);
// 3.创建连接与设置连接参数
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(postUrl);
StringEntity entity = new StringEntity(json);
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
// 4.发起请求与接收返回值
HttpResponse response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() != 200) {
throw new Exception("调用服务端异常.");
}
HttpEntity res = response.getEntity();
String resultData = EntityUtils.toString(res);
System.out.println("从服务端返回结果: " + resultData);
// 5.关闭连接
httpClient.close();
}
4.okhttp中的OkHttpClient
引入依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
</exclusion>
</exclusions>
</dependency>
public static void postTest() throws Exception {
// 1.请求URL
String postUrl = "";
// 2.请求参数
Map<String, String> paramMap = new HashMap<>();
String json = JSON.toJSONString(paramMap);
// 3.创建连接与设置连接参数
MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
RequestBody requestBody = RequestBody.Companion.create(json, mediaType);
Request request = new Request.Builder().url(postUrl).post(requestBody).build();
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
// 4.发起请求与接收返回值
Response response = okHttpClient.newCall(request).execute();
String resultData = response.body().string();
System.out.println("从服务端返回结果: " + resultData);
}
5.RestTemplate
RestTemplate:RestTemplate 是 Spring 提供的一个经典同步 HTTP 客户端工具,可以用于调用 RESTful 风格的外部接口,代码简单,默认依赖jdk的HTTP连接工具。
RestTemplate restTemplate = new RestTemplate();
String url = "http://api.example.com/user/{id}";
Map<String, String> params = new HashMap<>();
params.put("id", "123");
User user = restTemplate.getForObject(url, User.class, params);
使用 RestTemplate并发调用:
public class RestTemplateConcurrentExample {
private RestTemplate restTemplate = new RestTemplate();
public void fetchMultipleUsers(String[] userIds) {
ExecutorService executor = Executors.newFixedThreadPool(userIds.length);
for (String userId : userIds) {
executor.submit(() -> {
String url = "https://api.example.com/users/" + userId;
String response = restTemplate.getForObject(url, String.class);
System.out.println(response);
});
}
executor.shutdown();
}
}
6.WebClient
WebClient 是 Spring 5 引入的一种非阻塞式、响应式的 HTTP 客户端工具,它提供了一套简洁的 API 来发送 HTTP 请求并处理响应。WebClient 基于 Reactor 提供了对响应式编程的支持,可以实现高性能的异步操作。
1.简单使用
WebClient webClient = WebClient.create();
String url = "http://api.example.com/user/{id}";
Map<String, String> params = new HashMap<>();
params.put("id", "123");
Mono<User> result = webClient.get()
.uri(uriBuilder -> uriBuilder.path(url).build(params))
.retrieve()
.bodyToMono(User.class);
result.subscribe(user -> {
// 处理响应结果
});
webclient并发调用:
public class WebClientConcurrentExample {
private WebClient webClient = WebClient.create();
public Flux<String> fetchMultipleUsers(String[] userIds) {
return Flux.fromArray(userIds)
.flatMap(userId -> webClient.get()
.uri("https://api.example.com/users/" + userId)
.retrieve()
.bodyToMono(String.class));
}
}
2.高级使用
1.配置连接池
/**
WebClient连接池
**/
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
// 配置HTTP连接池
ConnectionProvider provider = ConnectionProvider.builder("custom")
.maxConnections(500)
.maxIdleTime(Duration.ofSeconds(20))
.build();
// 配置HTTP客户端
HttpClient httpClient = HttpClient.create(provider)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.responseTimeout(Duration.ofSeconds(5))
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(5))
.addHandlerLast(new WriteTimeoutHandler(5)));
// 构建WebClient实例
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.baseUrl("https://echo.apifox.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
// 添加请求日志记录功能
.filter(ExchangeFilterFunction.ofRequestProcessor(
clientRequest -> {
log.debug("Request: {} {}",
clientRequest.method(),
clientRequest.url());
return Mono.just(clientRequest);
}
))
// 添加响应日志记录功能
.filter(ExchangeFilterFunction.ofResponseProcessor(
clientResponse -> {
log.debug("Response status: {}",
clientResponse.statusCode());
return Mono.just(clientResponse);
}
))
.build();
}
}
2. retrieve()和exchange()区别
retrieve()
- 用途:retrieve() 方法用于简化响应处理,特别是当你只需要响应体时。
- 自动错误处理:retrieve() 会自动处理 HTTP 错误状态码(例如 4xx 和 5xx),并抛出 WebClientResponseException 及其子类。
- 返回值:通常用于直接获取响应体,例如 bodyToMono(String.class) 或 bodyToFlux(String.class)。
- 适用场景:适用于大多数常见的请求处理场景,特别是当你不需要手动处理响应状态码时。
public Mono<JSONObject> get(String q1) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/get")
.queryParam("q1", q1)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JSONObject.class);
}
exchange()
- 用途:exchange() 方法提供了更底层的控制,允许你手动处理响应,包括响应状态码和响应头。
- 手动错误处理:exchange() 不会自动处理 HTTP 错误状态码,你需要手动检查响应状态码并进行相应的处理。
- 返回值:返回 ClientResponse 对象,你可以从中提取响应状态码、响应头和响应体。
- 适用场景:适用于需要手动处理响应状态码或响应头的复杂场景。
public Mono<JSONObject> get(String q1) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/get")
.queryParam("q1", q1)
.build())
.accept(MediaType.APPLICATION_JSON)
.exchangeToMono(response -> {
if (response.statusCode().is2xxSuccessful()) {
return response.bodyToMono(JSONObject.class);
} else {
return Mono.error(new RuntimeException("Request failed with status code: " + response.statusCode()));
}
});
}
3. GET,POST,PUT,DELETE请求
@Service
public class ApiService {
@Resource
private WebClient webClient;
// GET请求
public Mono<JSONObject> get(String q1) {
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/get")
.queryParam("q1", q1)
.build())
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JSONObject.class);
}
// POST请求
public Mono<JSONObject> post(JSONObject body) {
return webClient.post()
.uri("/post")
.bodyValue(body)
.retrieve()
.bodyToMono(JSONObject.class);
}
// PUT请求
public Mono<JSONObject> put(String q1, JSONObject JSONObject) {
return webClient.put()
.uri(uriBuilder -> uriBuilder
.path("/put")
.queryParam("q1", q1)
.build())
.bodyValue(JSONObject)
.retrieve()
.bodyToMono(JSONObject.class);
}
// DELETE请求
public Mono<JSONObject> delete(String q1) {
return webClient.delete()
.uri(uriBuilder -> uriBuilder
.path("/delete")
.queryParam("q1", q1)
.build())
.retrieve()
.bodyToMono(JSONObject.class);
}
}
4.异常处理
onStatus:
用于处理HTTP响应状态码,允许根据不同状态码执行特定逻辑。例如,当收到404状态码时重定向到错误页面,或根据200状态码正常处理数据。
onErrorResume:
用于处理异常情况(如超时、连接失败等),提供备用响应。例如,当请求超时时返回默认数据或重试请求。
核心差异:
触发时机:onStatus在响应阶段触发,onErrorResume在请求阶段触发。
处理范围:onStatus仅处理HTTP状态码异常,onErrorResume处理所有请求异常(包括网络问题、超时等)。
链式操作:onErrorResume支持链式调用(如重试、返回默认值等),onStatus通常仅用于分支处理。
// 处理错误响应
public Mono<JSONObject> getUserWithErrorHandling(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("客户端错误")))
.onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("服务器错误")))
.bodyToMono(JSONObject.class);
}
Mono<ResponseEntity<String>> mono = WebClient.create().get().uri("http://")
.retrieve()
.toEntity(String.class)
.onErrorResume(WebClientResponseException.class, e -> {
if (e.getStatusCode().is4xxClientError()) {
return Mono.error(new HttpClientErrorException(e.getStatusCode(), e.getResponseBodyAsString()));
}
return Mono.error(e);
});
7.OpenFeign或者Feign
Feign 是 Spring Cloud 提供的一个声明式的 HTTP 客户端工具,它基于注解和接口定义的方式,简化了外部接口调用的流程。Feign 集成了 Ribbon 负载均衡和 Hystrix 熔断器等功能,使得接口调用更加灵活可靠。
@FeignClient(name = "user-service", url = "http://api.example.com")
public interface UserFeignClient {
@GetMapping("/user/{id}")
User getUser(@PathVariable("id") String id);
}
总结:
1.WebClient与RestTemplate对比
特性 | WebClient | RestTemplate |
---|---|---|
编程模型 |
|
|
性能 | 更好 | 一般 |
资源利用 | 更高效 | 一般 |
学习曲线 | 较陡 | 平缓 |
适用场景 | 高并发、响应式系统 | 简单应用、传统系统 |
参考