Http客户端工具类

参考文章:
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值