一个Http客户端

世面上有很多很多好用的客户端,如HttpClient,OkHttp等,如果您已经用惯了其他的客户端,那么可以继续用,如果您在编写Http请求的时候,感觉要写的代码很散,或者要写的内容很多,很复杂,那么您可以尝试一下RestBuilder,他设计的初衷就是通过傻瓜式链式调用,完成http请求。

  1. hello RestBuilder

发送一个非常简单的http请求

String response = 
            RestBuilder
        .builder()
                .sendForObj(HttpMethod.GET, "http://www.baidu.com", String.class);
  1. get请求路径url参数拼接
String response =
        RestBuilder
                .builder()
                .pathParam("age","10")
                .pathParam("name","xiaoming")
                .sendForObj(HttpMethod.GET, "http://www.baidu.com", String.class);

拼接后的url

http://www.baidu.com?name=xiaoming&age=10

  1. 基于restful风格的参数拼接
String response =
        RestBuilder
                .builder()
                .restfulPathParam("age","10")
                .restfulPathParam("name","xiaoming")
                .sendForObj(HttpMethod.GET, "http://www.baidu.com/${age}/${name}", String.class);

拼接后的url

http://www.baidu.com/10/xiaoming

  1. 设置Content-type &添加Header
String response =
        RestBuilder
                .builder()
                .contentType(MediaType.APPLICATION_JSON)
                .header("token","xxxxxxxxxxxxxxx")
                .sendForObj(HttpMethod.GET, "http://www.baidu.com", String.class);
  1. 表单请求
String response =
        RestBuilder
                .builder()
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .formData("username","xiaoming")
                .formData("password","123456")
                .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);

http请求信息

HTTP POST http://www.baidu.com
Accept=[text/plain, application/json, application/*+json, /]
Writing [{username=[xiaoming], password=[123456]}] as "application/x-www-form-urlencoded"

  1. 添加body

    1. 方式一

      不断添加k,v参数,最终会整合成一个json对象

    String response =
            RestBuilder
                    .builder()
                    .contentType(MediaType.APPLICATION_JSON)
                    .bodyParam("username","xiaoming")
                    .bodyParam("password","123456")
                    .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
    

    http请求信息

    HTTP POST http://www.baidu.com
    Accept=[text/plain, application/json, application/*+json, /]
    Writing [{"password":"123456","username":"xiaoming"}] as "application/json"

    1. 方式二

      直接传入对象,如果是String类型,直接当做body,如果是其他类型,最终转换成json形式

    String response =
            RestBuilder
                    .builder()
                    .contentType(MediaType.APPLICATION_JSON)
                    .bodyObj(() -> User.builder()
                                    .name("xiaoming")
                                    .password("123456")
                                    .build())
                    .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
    
    1. 添加监控日志

    调用链中加入monitor()方法

    String response =
            RestBuilder
                    .builder()
                    .contentType(MediaType.APPLICATION_JSON)
                    .bodyObj(() -> User.builder()
                                    .name("xiaoming")
                                    .password("123456")
                                    .build())
                    .monitor()
                    .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
    
    1. 设置readTimeout&connectTimeout
    String response =
            RestBuilder
                    .builder()
                    .timeout(2000,2000)
                    .sendForObj(HttpMethod.POST, "http://www.baidu.com", String.class);
    
    1. 获取响应的Http状态码,响应头等信息

      sendForObj换成sendForEntity

    ResponseEntity responseEntity =
            RestBuilder
                    .builder()
                    .timeout(2000, 2000)
                    .sendForEntity(HttpMethod.POST, "http://www.baidu.com", String.class);
    
    1. 获取响应流

      一般用于文件下载使用,将sendForObj换成sendForInputStream即可。

    InputStream inputStream = RestBuilder
            .builder()
            .timeout(2000, 2000)
            .sendForInputStream(HttpMethod.POST, "http://www.baidu.com");
    
    1. 复杂响应类型

      获取的响应类型形如:Map>>形式,这时我们可以使用JDK提供的TypeToken设置响应类型

    Type type = new TypeToken>>() {}.getType();
    HashMap> response =
            RestBuilder
                    .builder()
                    .timeout(2000,2000)
                    .sendForObj(HttpMethod.POST, "http://www.baidu.com", type);
    

[注]

  • 底层封装的是RestTemplate,使用到的所有Http相关API均来自Spring
  • RestTemplate不会频繁的被创建,如果不设置超时时间,那么使用默认的restTemplate对象,如果设置了超时时间会尝试从缓存池中获取restTemplate对象,如果不存在,才会创建restTempalte对象。
  1. 这里需要添加gson依赖,用于json处理,
        
            com.google.code.gson
            gson
            2.8.6
        
        
            org.projectlombok
            lombok
            true
        
  1. 代码
@Slf4j
public class RestBuilder {

    private static final RestTemplate REST_TEMPLATE;

    private HttpHeaders httpHeaders = new HttpHeaders();

    private String postBodyStr;

    private Map restfulPathParamMap;

    private Map getParamMap;

    private Map bodyParam;

    private MultiValueMap formData;

    private HttpEntity httpEntity;

    private String requestPath;

    private Integer readTimeout;

    private Integer connectTimeout;

    private Boolean setTimeout = false;

    private Boolean monitor = false;

    private static final Cache CACHE;

    private static final Object LOCK = new Object();

    static {
        REST_TEMPLATE = new RestTemplate();
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
        REST_TEMPLATE.getMessageConverters().add(converter);
        CACHE = CacheBuilder.newBuilder()
                .maximumSize(50)
                .expireAfterWrite(30, TimeUnit.MINUTES)
                .build();
    }

    private RestBuilder() {
    }

    public static RestBuilder builder() {
        return new RestBuilder();
    }

    /**
     * 添加header
     *
     * @param headerKey key
     * @param value     value
     * @return {@link RestBuilder}
     */
    public RestBuilder header(String headerKey, String value) {
        Assert.hasText(headerKey, "headerKey must not be null");
        httpHeaders.add(headerKey, value);
        return this;
    }

    /**
     * 设置Http ContentType
     *
     * @param mediaType {@link MediaType}
     * @return {@link RestBuilder}
     */
    public RestBuilder contentType(MediaType mediaType) {
        if (Objects.isNull(httpHeaders)) {
            httpHeaders = new HttpHeaders();
        }
        httpHeaders.setContentType(mediaType);
        return this;
    }

    /**
     * 设置GET请求参数,URL拼接形式
     *
     * @param pathParam key
     * @param value     value
     * @return {@link RestBuilder}
     */
    public RestBuilder pathParam(String pathParam, String value) {
        Assert.hasText(pathParam, "supplier must not be null");
        if (Objects.isNull(getParamMap)) {
            getParamMap = new HashMap<>();
        }
        getParamMap.put(pathParam, value);
        return this;
    }

    /**
     * 设置post表单
     *
     * @param param key
     * @param value value
     * @return {@link RestBuilder}
     */
    public RestBuilder formData(String param, String value) {
        Assert.hasText(param, "supplier not be null");
        if (Objects.isNull(this.formData)) {
            formData = new LinkedMultiValueMap<>();
        }
        this.formData.add(param, value);
        return this;
    }

    /**
     * 设置body参数,最终会被转换成json
     *
     * @param key   key
     * @param value value
     * @return {@link RestBuilder}
     */
    public  RestBuilder bodyParam(String key, String value) {
        Assert.hasText(key, "function must not be null");
        if (Objects.isNull(bodyParam)) {
            this.bodyParam = new HashMap<>();
        }
        bodyParam.put(key, value);
        return this;
    }

    /**
     * 设置请求body,最终会转换成json
     *
     * @param supplier {@link Object}
     * @return {@link RestBuilder}
     */
    public RestBuilder bodyObj(Supplier supplier) {
        Assert.notNull(supplier, "supplier must not be null");
        Object obj = supplier.get();
        this.postBodyStr = obj instanceof String ? (String) obj : GsonUtils.object2Json(obj);
        return this;
    }

    /**
     * 设置路径参数
     *
     * @param key   key
     * @param value value
     * @return {@link RestBuilder}
     */
    public RestBuilder restfulPathParam(String key, String value) {
        Assert.hasText(key, "key must not be null");
        if (Objects.isNull(restfulPathParamMap)) {
            this.restfulPathParamMap = new HashMap<>();
        }
        restfulPathParamMap.put(key, value);
        return this;
    }

    /**
     * 发送请求并返回{@link ResponseEntity}
     *
     * @param requestPath  请求路径/url
     * @param responseType 响应类型
     * @param           ResponseType
     * @return response
     */
    public  ResponseEntity sendForEntity(HttpMethod requestMethod, String requestPath, Type responseType) {
        build(requestPath);
        return requestForEntity(this.requestPath, this.httpEntity, responseType, requestMethod);
    }

    /**
     * 发送请求并直接返回响应体
     *
     * @param requestMethod {@link HttpMethod}
     * @param requestPath   请求路径
     * @param responseType  响应类型
     * @param            R
     * @return response Body
     */
    public  R sendForObj(HttpMethod requestMethod, String requestPath, Type responseType) {
        ResponseEntity responseEntity = sendForEntity(requestMethod, requestPath, responseType);
        return responseEntity.getBody();
    }

    /**
     * 发送请求并返回{@link ResponseEntity}
     *
     * @param requestPath  请求路径/url
     * @param responseType 响应类型
     * @param           ResponseType
     * @return response
     */
    public  ResponseEntity sendForEntity(HttpMethod requestMethod, String requestPath, Class responseType) {
        build(requestPath);
        return requestForEntity(this.requestPath, this.httpEntity, responseType, requestMethod);
    }

    /**
     * 发送请求并直接返回响应体
     *
     * @param requestMethod {@link HttpMethod}
     * @param requestPath   请求路径
     * @param responseType  响应类型
     * @param            R
     * @return response Body
     */
    public  R sendForObj(HttpMethod requestMethod, String requestPath, Class responseType) {
        ResponseEntity responseEntity = sendForEntity(requestMethod, requestPath, responseType);
        return responseEntity.getBody();
    }

    /**
     * 发送GET请求并反返回响应流
     *
     * @param requestPath 请求路径/URL
     * @return InputStream
     */
    public InputStream sendForInputStream(HttpMethod requestMethod, String requestPath) throws IOException {
        ResponseEntity responseEntity = sendForEntity(requestMethod, requestPath, Resource.class);
        return Objects.requireNonNull(responseEntity.getBody()).getInputStream();
    }

    /**
     * 发送请求检测
     *
     * @return this
     */
    public RestBuilder monitor() {
        this.monitor = true;
        return this;
    }

    /**
     * 设置readTimeOut
     *
     * @param connectTimeout connectTimeout
     * @param readTimeout    readTimeout
     * @return {@link RestBuilder}
     */
    public RestBuilder timeout(int readTimeout, int connectTimeout) {
        this.setTimeout = true;
        this.readTimeout = readTimeout;
        this.connectTimeout = connectTimeout;
        return this;
    }

    /**
     * 构造restful路径
     *
     * @param path path
     * @return restful path
     */
    private String generatePath(String path) {
        if (Objects.nonNull(restfulPathParamMap) && !restfulPathParamMap.isEmpty()) {
            // 替换restful值
            Set> entrySet = restfulPathParamMap.entrySet();
            for (Map.Entry entry : entrySet) {
                path = path.replace(String.format("${%s}", entry.getKey()), entry.getValue());
            }
        }
        if (Objects.nonNull(getParamMap) && !getParamMap.isEmpty()) {
            StringBuilder pathBuilder = new StringBuilder(path).append("?");
            // 拼接请求值
            getParamMap.forEach((k, v) -> pathBuilder.append(k).append("=").append(v).append("&"));
            // 最后一个&
            int length = pathBuilder.length();
            pathBuilder.delete(length - 1, length);
            path = pathBuilder.toString();
        }
        if (monitor) {
            log.info("PATH [ {} ]", path);
        }
        return path;
    }

    /**
     * 构造http的URL和body
     *
     * @param path 请求路径
     */
    private void build(String path) {
        // 构造请求路径
        this.requestPath = generatePath(path);
        Object body = null;

        // 表单和body只能选中一个
        Assert.isTrue(!(formData != null && (postBodyStr != null || bodyParam != null)),
                "body or form data only one can be selected");

        // 没有指定contentType默认'application/json'
        MediaType contentType = httpHeaders.getContentType();
        if (Objects.isNull(formData)) {
            if (Objects.isNull(contentType)) {
                httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            }
            body = Strings.isNullOrEmpty(postBodyStr) ? GsonUtils.object2Json(bodyParam) : postBodyStr;
        } else {
            httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            body = formData;
        }
        // body允许传空body
        this.httpEntity = new HttpEntity<>(body, httpHeaders);
        if (this.monitor) {
            log.info("requestEntity [ {} ]", httpEntity.toString());
        }
    }

    private  ResponseEntity requestForEntity(String url, @Nullable Object request, Type responseType, HttpMethod httpMethod, Object... uriVariables) throws RestClientException {
        long begin = 0;
        if (monitor) {
            begin = System.currentTimeMillis();
        }
        RestTemplate restTemplate = getRestTemplate();
        RequestCallback requestCallback = restTemplate.httpEntityCallback(request, responseType);
        ResponseExtractor> responseExtractor = restTemplate.responseEntityExtractor(responseType);
        ResponseEntity responseEntity = nonNull(restTemplate.execute(url, httpMethod, requestCallback, responseExtractor, uriVariables));
        if (monitor) {
            log.info("Response Message [{}]", responseEntity);
            log.info("cost time [{}] ms.", System.currentTimeMillis() - begin);
        }
        return responseEntity;
    }

    private  T nonNull(@Nullable T result) {
        Assert.state(result != null, "No result");
        return result;
    }

    /**
     * 获取restTemplate
     *
     * @return RestTemplate
     */
    private RestTemplate getRestTemplate() {
        if (!setTimeout) {
            return RestBuilder.REST_TEMPLATE;
        }
        synchronized (LOCK) {
            // 先去查看是否已经缓存了相同设置超时时间的restTemplate
            // 拼接规则为 ${readTimeout.toString()}:${connectTimeout.toString()}
            String cacheKey = generateCacheKey();
            RestTemplate timoutRestTemplate = CACHE.getIfPresent(cacheKey);
            if (Objects.nonNull(timoutRestTemplate)) {
                // 重置超时时间
                CACHE.put(cacheKey, timoutRestTemplate);
                return timoutRestTemplate;
            }
            // 之前没有缓存该restTemplate,生成好restTemplate,然后缓存起来
            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            if (Objects.nonNull(this.readTimeout)) {
                requestFactory.setReadTimeout(readTimeout);
            }
            if (Objects.nonNull(this.connectTimeout)) {
                requestFactory.setConnectTimeout(connectTimeout);
            }
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
            restTemplate.getMessageConverters().add(converter);
            CACHE.put(cacheKey, restTemplate);
            return restTemplate;
        }
    }

    private String generateCacheKey() {
        StringBuilder sb = new StringBuilder();
        if (Objects.nonNull(readTimeout)) {
            sb.append(readTimeout.toString());
        }
        if (Objects.nonNull(connectTimeout)) {
            sb.append(connectTimeout.toString());
        }
        return sb.toString();
    }
}

 

                            
                        
                    
                    
                    

你可能感兴趣的:(一个Http客户端)