参考文章:
https://www.baeldung.com/guide-to-okhttp
https://www.baeldung.com/rest-template
一、第三方工具类
1.1 OkHttp
OkHttp 是一个高效的 HTTP 客户端,它被广泛用于 Android 和 Java 应用程序中以发送网络请求。
添加依赖:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
1.1.1 Get 请求
import okhttp3.*;
import java.io.IOException;
public class OkHttpGetExample {
private final OkHttpClient okHttpClient;
// RequestHeaders 、RequestParams 就是一个Map的封装类
public <T> T executeGetRequest(String url, RequestHeaders headers, RequestParams params, Class<T> responseType) {
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
if (params != null) {
for (Map.Entry<String, Object> entry : params.getParams().entrySet()) {
urlBuilder.addQueryParameter(entry.getKey(), entry.getValue().toString());
}
}
Request.Builder requestBuilder = new Request.Builder()
.url(urlBuilder.build());
if (headers != null) {
requestBuilder.headers(Headers.of(headers.getHeaders()));
}
Request request = requestBuilder.build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new HttpClientException("请求失败: " + response.code(),null);
}
String responseBody = response.body().string();
return parseResponse(responseBody, responseType);
} catch (IOException e) {
throw new HttpClientException("请求失败: " + e.getMessage(),e);
}
}
public void get(String url, RequestHeaders headers, RequestParams params, Callback callback) {
Request request = new Request.Builder()
.url(buildUrlWithParams(url, params))
.headers(Headers.of(headers.getHeaders()))
.build();
// 异步请求
client.newCall(request).enqueue(callback);
}
}
1.1.2 Post 请求
import okhttp3.*;
public class OkHttpPostExample {
private final OkHttpClient okHttpClient;
public <T> T executePostJsonRequest(String url, RequestHeaders headers, String requestBody, Class<T> responseType) {
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), requestBody);
Request.Builder requestBuilder = new Request.Builder()
.url(url)
.post(body);
if (headers != null) {
requestBuilder.headers(Headers.of(headers.getHeaders()));
}
Request request = requestBuilder.build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new HttpClientException("请求失败: " + response.code(),null);
}
String responseBody = response.body().string();
return parseResponse(responseBody, responseType);
} catch (IOException e) {
throw new HttpClientException("请求失败: " + e.getMessage(),e);
}
}
public <T> T executePostFormRequest(String url, RequestHeaders headers, RequestParams formData, Class<T> responseType) {
FormBody.Builder formBodyBuilder = new FormBody.Builder();
if (formData != null) {
for (Map.Entry<String, Object> entry : formData.getParams().entrySet()) {
formBodyBuilder.add(entry.getKey(), entry.getValue().toString());
}
}
RequestBody body = formBodyBuilder.build();
Request.Builder requestBuilder = new Request.Builder()
.url(url)
.post(body);
if (headers != null) {
requestBuilder.headers(Headers.of(headers.getHeaders()));
}
Request request = requestBuilder.build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new HttpClientException("请求失败: " + response.code(),null);
}
String responseBody = response.body().string();
return parseResponse(responseBody, responseType);
} catch (IOException e) {
throw new HttpClientException("请求失败: " + e.getMessage(),e);
}
}
public void postJson(String url, RequestHeaders headers, String requestBody, Callback callback) {
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), requestBody);
Request request = new Request.Builder()
.url(url)
.headers(Headers.of(headers.getHeaders()))
.post(body)
.build();
// 异步
client.newCall(request).enqueue(callback);
}
public void postForm(String url, RequestParams formData, Callback callback) {
FormBody.Builder formBuilder = new FormBody.Builder();
for (Map.Entry<String, Object> entry : formData.getParams().entrySet()) {
formBuilder.add(entry.getKey(), entry.getValue().toString());
}
RequestBody body = formBuilder.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
}
1.2 RestTemplate
RestTemplate 是 Spring 框架中用于发送 HTTP 请求的类。
1.2.1 Get 请求
public class RestTemplateGetExample {
public void doGet() {
RestTemplate restTemplate = new RestTemplate();
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer token_value");
headers.set("Custom-Header", "CustomValue");
// 构建 HttpEntity,用于发送包含请求头的信息
HttpEntity<String> entity = new HttpEntity<>(headers);
// 发送 GET 请求,获取响应
String url = "https://api.example.com/data";
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
// 打印响应结果
System.out.println(response.getBody());
}
public void doGetAsync() {
WebClient webClient = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader("Authorization", "Bearer token_value")
.build();
// 异步 GET 请求
Mono<String> responseMono = webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class);
// 处理异步响应
responseMono.subscribe(response -> {
System.out.println("Response: " + response);
});
// 阻塞调用 - 仅用于演示,如果需要同步执行结果
String response = responseMono.block();
System.out.println("Synchronous response: " + response);
}
}
1.2.2 Post请求
public class RestTemplatePostExample {
public void doPost() {
RestTemplate restTemplate = new RestTemplate();
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer token_value");
// 创建请求体
Map<String, String> requestBody = new HashMap<>();
requestBody.put("username", "john");
requestBody.put("password", "secret");
// 构建 HttpEntity,包含请求头和请求体
HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestBody, headers);
// 发送 POST 请求,获取响应
String url = "https://api.example.com/login";
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
// 打印响应结果
System.out.println(response.getBody());
}
}
1.3 Fegin
Feign 是一个声明式的 HTTP 客户端,提供了一种简洁的方式来调用 REST API,它通过注解定义接口的方法映射到远程 HTTP 请求上。Feign 的主要优点是集成了 Spring Cloud 和 Ribbon,方便实现负载均衡、自动重试等功能。
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
开启 Feign 客户端
@SpringBootApplication
@EnableFeignClients
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
接口定义
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "userService", url = "https://api.example.com")
public interface UserClient {
@GetMapping("/api/users/{id}")
User getUserById(@PathVariable("id") Long id);
@PostMapping("/api/users")
User createUser(@RequestBody User user);
@GetMapping("/api/users/{id}")
User getUserById(@PathVariable("id") Long id, @RequestHeader("Authorization") String token);
}
● @FeignClient:声明这是一个 Feign 客户端,name 是客户端的名称,url 是远程服务的 URL。可以将 url 设置为动态的配置项。
● @GetMapping:指定 HTTP 请求类型,类似于 Spring MVC 的注解。
● @PathVariable:指定路径变量。
二、自定义封装
项目开发,统一调用方式。
2.1静态方法方案(不推荐)
public class HttpUtil {
public static <T> T get(String url, Map<String, String> headers, Map<String, String> params, Class<T> responseType) throws IOException {
//实现
}
}
2.2 通用接口设计方案
静态方法更进一步的设计方案。面向 接口开发,隐藏实现细节,更易维护。
可以使用 Builder 模式 封装请求参数、请求头。
利用 重载 可以设计更多的接口满足业务需求。
但是维护起来还是比较麻烦的,方法太多了。
同步调用接口
package com.github.nan.http;
import com.github.nan.http.dto.RequestHeaders;
import com.github.nan.http.dto.RequestParams;
/**
* 同步http客户端
*
* 如果请求没有返回结果,responseType 建议使用 Void.class
*
* @author NanNan Wang
*/
public interface SyncHttpClient {
/**
* 发起一个简单的 GET 请求。
*
* @param url 请求的 URL 地址
* @param responseType 返回的结果类型
* @param <T> 泛型类型
* @return 请求的结果,类型为 T
*/
<T> T get(String url, Class<T> responseType);
/**
* 发起一个带有参数的 GET 请求。
*
* @param url 请求的 URL 地址
* @param params 请求的参数
* @param responseType 返回的结果类型
* @param <T> 泛型类型
* @return 请求的结果,类型为 T
*/
<T> T get(String url, RequestParams params, Class<T> responseType);
/**
* 发起一个带有头部的 GET 请求。
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param responseType 返回的结果类型
* @return 泛型类型
* @param <T> 请求的结果,类型为 T
*/
<T> T get(String url, RequestHeaders headers, Class<T> responseType);
/**
* 发起一个带有头部和参数的 GET 请求。
*
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param params 请求的参数
* @param responseType 返回的结果类型
* @param <T> 泛型类型
* @return 请求的结果,类型为 T
*/
<T> T get(String url, RequestHeaders headers, RequestParams params, Class<T> responseType);
/**
* 发起一个带有 JSON 请求参数的 POST 请求。
*
* @param url 请求的 URL 地址
* @param requestBody JSON 请求体
* @param responseType 返回的结果类型
* @param <T> 泛型类型
* @return 请求的结果,类型为 T
*/
<T> T postJson(String url, String requestBody, Class<T> responseType);
/**
* 发起一个带有 JSON 请求参数的 POST 请求。
*
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param requestBody JSON 请求体
* @param responseType 返回的结果类型
* @param <T> 泛型类型
* @return 请求的结果,类型为 T
*/
<T> T postJson(String url, RequestHeaders headers, String requestBody, Class<T> responseType);
/**
* 发起一个带有表单数据的 POST 请求。
*
* @param url 请求的 URL 地址
* @param formData 表单数据
* @param responseType 返回的结果类型
* @param <T> 泛型类型
* @return 请求的结果,类型为 T
*/
<T> T postForm(String url, RequestParams formData, Class<T> responseType);
/**
* 发起一个带有表单数据的 POST 请求。
*
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param formData 表单数据
* @param responseType 返回的结果类型
* @param <T> 泛型类型
* @return 请求的结果,类型为 T
*/
<T> T postForm(String url, RequestHeaders headers, RequestParams formData, Class<T> responseType);
}
异步调用接口
/**
* 异步http客户端
*
* @author NanNan Wang
*/
public interface AsyncHttpClient {
/**
* 发起异步 GET 请求。
*
* @param url 请求 URL
* @param callback 回调函数
*/
void get(String url, Callback callback);
/**
* 发起一个带有头部的 GET 请求。
*
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param callback 回调函数
*/
void get(String url, RequestHeaders headers, Callback callback);
/**
* 发起一个带有参数的 GET 请求。
*
* @param url 请求的 URL 地址
* @param params 请求的参数
* @param callback 回调函数
*/
void get(String url, RequestParams params, Callback callback);
/**
* 发起一个带有头部和参数的 GET 请求。
*
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param params 请求的参数
* @param callback 回调函数
*/
void get(String url, RequestHeaders headers, RequestParams params, Callback callback);
/**
* 发起一个带有 JSON 请求参数的 POST 请求。
*
* @param url 请求的 URL 地址
* @param requestBody JSON 请求体
* @param callback 回调函数
*/
void postJson(String url, String requestBody, Callback callback);
/**
* 发起一个带有 JSON 请求参数和头部信息的 POST 请求。
*
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param requestBody JSON 请求体
* @param callback 回调函数
*/
void postJson(String url, RequestHeaders headers, String requestBody, Callback callback);
/**
* 发起一个带有表单数据的 POST 请求。
*
* @param url 请求的 URL 地址
* @param formData 表单数据
* @param callback 回调函数
*/
void postForm(String url, RequestParams formData, Callback callback);
/**
* 发起一个带有表单数据和头部信息的 POST 请求。
*
* @param url 请求的 URL 地址
* @param headers 请求的头部信息
* @param formData 表单数据
* @param callback 回调函数
*/
void postForm(String url, RequestHeaders headers, RequestParams formData, Callback callback);
}
2.3 链式客户端
在第二个方案上进一步优化,模仿 Stream ,设计一个链式调用接口。
更易维护和使用。
public interface HttpClient {
// 设置URL
HttpClient url(String url);
// 指定请求方法,为了代码可读性,建议调用时显示声明
HttpClient method(HttpMethod method);
// 添加请求头
HttpClient addHeader(Map<String, String> headers);
// 添加请求头
HttpClient addHeader(String key, String value);
// 设置请求参数(适用于 GET 请求)
HttpClient queryParam(String key, String value);
// 设置JSON请求体(适用于 POST、PUT 等请求)
HttpClient jsonBody(String json);
// 设置Form表单请求体
HttpClient formBody(Map<String, String> formData);
// 设置Form表单请求参数
HttpClient formBody(String key, String value);
// 执行请求
<T> T execute(Class<T> responseType);
// 异步请求执行,带有回调
<T> void executeAsync(Class<T> responseType, Callback<T> callback);
// 回调接口
interface Callback<T> {
void onSuccess(T response);
void onFailure(Exception e);
}
}
使用参考
public class Demo {
private HttpClient client = new DefaultHttpClient();
public void doGet() {
String response = client.url("https://api.example.com/data")
.method(HttpMethod.GET)
.addHeader("Authorization", "Bearer token_value")
.queryParam("filter", "active")
.execute(String.class);
System.out.println("Response: " + response);
}
public void doPost() {
String jsonRequestBody = "{\"name\": \"John\", \"age\": 30}";
String response = client.url("https://api.example.com/users")
.method(HttpMethod.POST)
.addHeader("Content-Type", "application/json")
.jsonBody(jsonRequestBody)
.execute(String.class);
System.out.println("Response: " + response);
}
public void doGetAsync() {
client.url("https://api.example.com/users")
.method(HttpMethod.GET)
.addHeader("Authorization", "Bearer token_value")
.executeAsync(String.class, new HttpClient.Callback<String>() {
@Override
public void onSuccess(String response) {
System.out.println("Async Response: " + response);
}
@Override
public void onFailure(Exception e) {
System.err.println("Request failed: " + e.getMessage());
}
});
}
}
具体实现类可以参考:
https://github.com/BJ-wnn/nanHub