http是第三方接口调用的常用工具,该组件通过对常见的http客户端工具进行统一的整合,参考了feign的上下文和springmvc的拦截器等相关设计思路,设计了相应的Context用于对请求上下文的相关配置进行统一的配置,避免客户端配置混乱配置,保证了项目的统一配置,并内置了相应拦截器,用于客户端自定义相关的业务逻辑,比如:通过模仿feign的拦截器,解决了feig三方接口调用时无token的尴尬.

一 基本原理

springboot oauth2 sso 客户端实现 springboot http客户端_http

  • 客户端发起http调用时,通过请求执行器,执行底层请求逻辑
  • 从请求的上下文中获得相应编码器,对body进行统一的序列化处理
  • 获得相应的前置处理器,执行自定义的业务前置处理(如token等公用参数的绑定)
  • 调用httprequestclient执行http请求(更具项目的自身需求,配置相应的客户端,默认为:JDK HttpURLConnection)
  • 请求发生异常,执行requestExceptionHandler中handler逻辑
  • 解析当前响应状态码,状态码不为200执行responseErrorHandler逻辑
  • 根据当前请求是否配置相应的响应前置处理器,做响应前置处理,没有直接通过解码器反序列化后相应当前请求的数据

二 客户端调用

2.1 根据项目的实际需求配置核心参数

详见源码具体配置

springboot oauth2 sso 客户端实现 springboot http客户端_spring boot_02

2.2 根据项目的实际需求选择相应的http客户端

springboot oauth2 sso 客户端实现 springboot http客户端_java_03

springboot oauth2 sso 客户端实现 springboot http客户端_http_04


springboot oauth2 sso 客户端实现 springboot http客户端_客户端_05


默认http客户端

springboot oauth2 sso 客户端实现 springboot http客户端_java_06

2.3 引入相应的组件

<dependency>
            <groupId>com.cncloud</groupId>
            <artifactId>shell-platform-common-http-client</artifactId>
 </dependency>

2.4 bean方式调用

@RestController
@RequestMapping("/client")
@Slf4j
public class HttpClientController {

    @Autowired
    private HttpRequestExecutor httpRequestExecutor;

    public static final String LOGIN_URL = "https://develop.shequtalk.com/auth/oauth/token";

    public static final String GET_URL = "https://develop.shequtalk.com/admin/user/getUserListByDept";



    @PostMapping("/testPost")
    @Inner(value = false)
    public void testPost(){
        HttpRequest request = getLoginRequest();
        final R result = httpRequestExecutor.execute(request);
        log.info("login result : {}",result);
    }

    @GetMapping("/testGet")
    @Inner(value = false)
    public void testGet(){
        HttpRequest request = new BasicRequestBuilder(GET_URL)
                .header("tenant-id", "6054656364413952")
                .header("authorization", "Bearer 8350ebce-1f98-41e3-a30f-69f4f6702119")
                .method(RequestMethod.GET)
                .query("sysDeptId", "6054656376194048")
                .query("current", 1)
                .query("size", 10)
                .resultType(R.class)
                .build();
        R result = httpRequestExecutor.execute(request);
        log.info("query result: {}",result);
    }
    private HttpRequest getLoginRequest() {
        HashMap<String, Object> bodyMap = new HashMap<>();
        bodyMap.put("username","szs");
        bodyMap.put("password","rKu1/348LvKp0rsVC06eCA==");
        bodyMap.put("grant_type","password");

        HttpRequest request = new BodyRequestBuilder<>(LOGIN_URL)
                .header("tenant-id", "6054656364413952")
                .header("authorization","Basic c2hlbGxwYzpzaGVsbHBj")
                .method(RequestMethod.POST)
                .form(bodyMap)
                .resultType(R.class)
                .build();
        return request;
    }
}

采用相应的构造者构建相应的请求

springboot oauth2 sso 客户端实现 springboot http客户端_http_07

2.5 非bean方式调用

@RestController
@RequestMapping("/client")
@Slf4j
public class HttpClientController {

    @Autowired
    private HttpRequestExecutor httpRequestExecutor;

    public static final String LOGIN_URL = "https://develop.shequtalk.com/auth/oauth/token";

    public static final String GET_URL = "https://develop.shequtalk.com/admin/user/getUserListByDept";
    
    @PostMapping("/testNoBeanWithHutool")
    @Inner(value = false)
    public void testNoBeanWithHutool(){
        HttpRequestClient httpRequestClient = new HutoolRequestClient();
        final HttpRequestExecutor httpRequestExecutor = initHttpRequestExecutor(httpRequestClient);
        HttpRequest request = getLoginRequest();
        final R result = httpRequestExecutor.execute(request);
        log.info("login result : {}",result);
    }

    private HttpRequestExecutor initHttpRequestExecutor(HttpRequestClient httpRequestClient) {

        return new DefaultHttpRequestExecutor(getContext(), httpRequestClient);
    }

    private Context getContext() {
        TimeoutSetting timeoutSetting = new TimeoutSetting();
        Encoder encoder = new DefaultEncoder();
        Decoder decoder = new DefaultDecoder();
        return new DefaultContext(timeoutSetting, decoder, encoder);
    }



    private HttpRequest getLoginRequest() {
        HashMap<String, Object> bodyMap = new HashMap<>();
        bodyMap.put("username","szs");
        bodyMap.put("password","rKu1/348LvKp0rsVC06eCA==");
        bodyMap.put("grant_type","password");

        HttpRequest request = new BodyRequestBuilder<>(LOGIN_URL)
                .header("tenant-id", "6054656364413952")
                .header("authorization","Basic c2hlbGxwYzpzaGVsbHBj")
                .method(RequestMethod.POST)
                .form(bodyMap)
                .resultType(R.class)
                .build();
        return request;
    }
}

三 核心API

/**
 * http请求执行器
 * @author likun
 * @date 2022/7/25 10:14
 */
public interface HttpRequestExecutor {
    /**
     *  执行http请求(异常处理器及前置拦截器均可传入)
     * @param request 核心请求
     * @param requestExceptionHandler 请求发送异常处理器
     * @param responseErrorHandler 相应异常处理器
     * @param requestInterceptor 请求拦截器
     * @param responseInterceptor 相应拦截器
     * @param <T>
     * @return
     */
    <T> T execute(HttpRequest request, RequestExceptionHandler requestExceptionHandler, ResponseErrorHandler responseErrorHandler,
                  RequestInterceptor requestInterceptor, ResponseInterceptor responseInterceptor);

    /**
     * 执行http请求(使用默认的拦截器和异常处理器)
     * @param request
     * @param <T>
     * @return
     */
    default <T> T execute(HttpRequest request){
        return execute(request,null,null,null,null);
    }

    /**
     * 执行http请求 (可以传入请求异常及响应状态码非200的请求处理器)
     * @param request
     * @param requestExceptionHandler
     * @param responseErrorHandler
     * @param <T>
     * @return
     */
    default <T> T execute(HttpRequest request, RequestExceptionHandler requestExceptionHandler, ResponseErrorHandler responseErrorHandler){
        return execute(request, requestExceptionHandler, responseErrorHandler, null, null);
    }

    /**
     * 执行http请求(可以传入请求前置拦截器及响应解码的前置拦截器)
     * @param request
     * @param requestInterceptor
     * @param responseInterceptor
     * @param <T>
     * @return
     */
    default <T> T execute(HttpRequest request, RequestInterceptor requestInterceptor, ResponseInterceptor responseInterceptor) {
        return execute(request, null, null, requestInterceptor, responseInterceptor);
    }


}

四 项目源码

由于改组件的源码较长,这里不在文章中展示,需要的可以私信笔者.