# JHttp **Repository Path**: steven_Jiang/JHttp ## Basic Information - **Project Name**: JHttp - **Description**: JHttp是用java编写的用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,基于原生的HttpUrlConnection实现,支持:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE请求,可以适应绝大部分业务场景,并提供了丰富的请求操作的API,使开发者更加方便快捷的完成HTTP的请求工作。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2019-11-04 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 第一部分 简介 `JHttp`是用`java` 编写的用来提供高效的、最新的、功能丰富的支持`HTTP`协议的客户端编程工具包,基于原生的`HttpUrlConnection`实现,支持:`GET`、`POST`、`PUT`、`DELETE`、`PATCH`、`HEAD`、`OPTIONS`、`TRACE`请求,可以适应绝大部分业务场景,并提供了丰富的请求操作的API,使开发者更加方便快捷的完成`HTTP`的请求工作。 # 第二部分 开始使用 使用`JHttp`可以直接下载源代码编译或者下载已经编译的`jar`文件,如果您是使用`maven`来构建项目,也可以直接在`pom.xml`中添加`JHttp`的坐标: [![Maven central](https://maven-badges.herokuapp.com/maven-central/com.jianggujin/JHttp/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.jianggujin/JHttp) ```xml com.jianggujin JHttp 最新版本 ``` 最新的版本可以从[Maven仓库](http://mvnrepository.com/artifact/com.jianggujin/JHttp)或者[码云](https://gitee.com/jianggujin)获取。 ## 2.1 一个例子 请求文本是最基本的用法,比如下载一个网页的源码或者普通的接口调用,以访问百度为例,我们可以这样写: ```java JResponse response = new JTextResponse(); JRequest request = JRequest.create("https://www.baidu.com").response(response); JRequestExecuter executer = new JDefaultRequestExecuter(); executer.execute(request); System.out.println(response.getData()); ``` 对于一次网络请求需要有请求执行器、请求对象与响应对象,这三部分构成了完整的请求流程。一般情况下每个请求都应该有响应,我们可以通过`JRequest.response(JResponse response)`设置响应的处理对象,上面的例子中使用的是`JTextResponse`,该类是文本响应的处理对象。除此之外,`JHttp`还提供了字节数组、文件等响应的处理。 默认情况下,`JRequest`使用`GET`请求方式,我们可以通过`JRequest.method(JMethod method)`设置请求方法,`JRequest`中还提供了一些常用的操作方法,比如超时时间、字符编码等。 ## 2.2 设置请求参数 通常情况,我们在网络请求的时候会传递一些请求参数,我们可以使用`JRequest`提供的`data`方法设置请求参数,该方法有几个重载方法,我们可以按照实际情况选择。 ```java String url = "http://ws.webxml.com.cn/WebServices/WeatherWS.asmx/getWeather"; JRequest request = JRequest.create(url).method(JMethod.POST).response(new JTextResponse()) .data("theCityCode", "2009").data("theUserID", ""); JRequestExecuter executer = new JDefaultRequestExecuter(); executer.execute(request); System.out.println(request.response().getData()); ``` ## 2.3 设置请求体 有些时候,我们可能不仅仅是传递普通参数,我们可能希望直接发送一段文本,比如`Restful`接口提交`JSON`格式数据,使用`JHttp`也可以很方便的处理这种需求,在`JRequest`中有`requestBody(Object body)`方法可以设置要提交的请求体,默认的请求体的处理支持:`CharSequence`、`Map`、`byte[]`和`char[]`。 我们可以这样使用: ```java String url = "http://ws.webxml.com.cn/WebServices/WeatherWS.asmx/getWeather"; JRequest request = JRequest.create(url).method(JMethod.POST).response(new JTextResponse()) .requestBody("theCityCode=2009&theUserID="); JRequestExecuter executer = new JDefaultRequestExecuter(); executer.execute(request); System.out.println(request.response().getData()); ``` 如果默认的实现不满足我们的需求,我们可以自定义自己的解析器,编写解析器我们只需要实现`com.jianggujin.http.request.JRequestBodyResolver`接口并通过`JRequest.requestBodyResolver(JRequestBodyResolver requestBodyResolver)`设置请求指定解析器,这种方式级别最高,如果不设置请求的解析器,请求执行器会尝试查找已经注册的解析器。注册的请求体解析器与请求执行器生命周相同,所以我们可以通过请求执行器获得相关注册器实现注册。 ```java executer.getRequestBodyResolverRegistrar().register(requestBodyResolver, true); ``` 该方法接收两个参数,第一个参数为请求体解析器对象,第二个参数为是否重写,即当出现同名解析器的时候是否替换,如果不替换则会抛出异常。`JHttp`提供了一个名为`default`的默认的请求体解析器,支持`CharSequence`、`Map`、`ByteBuffer`、`byte[]`和`char[]`的数据类型转换。 ## 2.4 请求响应处理 在`JRequest`中提供了`response(JResponse response)`方法用于设置响应处理对象,`JHttp`提供了如下几种响应实现: - JByteBufferResponse - 将响应解析为ByteBuffer - JByteResponse - 字节数组响应 - JFileResponse - 文件响应 - JJsonResponse - JSON响应 - JNoBodyResponse - 无响应体的响应 - JTextResponse - 文本响应 - JXMLDomResponse - XML响应,DOM方式解析 - JXmlResponse - XML响应 - JXMLSaxResponse - XML响应,SAX方式解析 需要注意的是`JJsonResponse`与`JXmlResponse`,这两个响应用于将响应的数据转换为指定的`Java Bean`,但是并未提供相关实现,如果我们需要这部分功能,可以实现相对应的`JJsonResolver`和`JXmlResolver`接口,并调用对应的`setResolver`方法设置解析实现,这样可以完成对默认响应的扩展。 > 如果默认提供的响应处理不满足实际需求,我们也可以实现`JResponse`接口,或者直接继承`JAbstractResponse`,按照实际需求处理响应。 ## 2.5 设置代理 如果程序运行环境存在网络限制,需要代理访问,我们只需要为请求设置代理对象,方法为`proxy(Proxy proxy)`, ```java Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port)); JRequest.create(url).proxy(proxy ); ``` ## 2.6 SSL 在处理`https`请求的时候,我们可能需要做`SSL`的设置,`JHttp`提供了`JSSLContextFactory`接口用于初始化`SSLContext`和`HostnameVerifier`,为了使用方便,`JHttp`提供了一个`JDefaultSSLContextFactory`,我们只需要传递协议算法即可,该类默认忽略证书的验证,并且信任所有主机,为了安全考虑,还是建议实现规范的处理。 ## 2.7 Cookie 如果我们需要做请求的会话保持,使用`JHttp`可以很方便的添加`Cookie`信息,在`JRequest`中,我们提供了几个`cookie`的重载方法,方便我们在请求之前添加`cookie`信息。 因为`JHttp`是基于`HttpUrlConnection`的,所以全局的`CookieManager`对于`JHttp`同样有效。 ```java CookieManager manager = new CookieManager(); CookieHandler.setDefault(manager); new JDefaultRequestExecuter().execute(JRequest.create("https://www.baidu.com")); for (HttpCookie cookie : manager.getCookieStore().getCookies()) { System.out.printf("%s=%s,domain=%s,path=%s\n", cookie.getName(), cookie.getValue(), cookie.getDomain(), cookie.getPath()); } ``` ## 2.8 请求回调 如果我们需要对请求添加日志等处理,我们可以实现`com.jianggujin.http.request.JRequestExecuterListener`接口,该接口会在请求执行前与请求之行结束进行相关方法的回调,方便我们做日志记录等处理,在`JHttp`中也提供了一个默认的日志实现类`com.jianggujin.http.request.impl.JRequestExecuterLog`,当然了,这个类仅仅是做了最简单的打印输出。 ```java executer.setRequestExecuterListener(new JRequestExecuterLog()); ``` 这种方式会对请求执行器的所有请求生效,如果想只针对某个请求生效,可以使用如下形式: ```java executer.execute(request, new JRequestExecuterLog()); ``` # 第三部分 扩展 `JHttp`除了上面的基本使用之外,还支持动态代理方式,通过定义`API`接口配合提供的注解,可以更加方便快捷的完成请求处理。使用接口形式,我们首先需要定义请求的接口,举个例子: ```java @JBaseUrl("http://ws.webxml.com.cn") public interface Weather { @JApiRequest(value = "/WebServices/WeatherWS.asmx/getWeather", method = JMethod.POST) String weather(@JRequestParam("theCityCode") String theCityCode, @JRequestParam("theUserID") String theUserID); } ``` 定义完接口,我们需要使用`JApiFactory`获得接口的代理实现: ```java JApiFactory apiFactory = new JApiFactory(); apiFactory.getApi(Weather.class).weather("2009", ""); ``` 接口方法中如果含有`JResponse`、`Proxy`、`JSSLContextFactory`、`JRequestBodyResolver`、`JMethod`、`JKeyVal`类型的参数,会直接将其设置在请求对象中。 ## 3.1 常用注解 ### 3.1.1 JApiRequest 设置API请求信息,用于设置请求`路径`、`方法`等信息,可以用在`方法`上面。与`JBaseUrl`搭配使用,会将域名与路径进行拼接。 ### 3.1.2 JApiRequestBodyResolver 请求体解析器,用于指定当前请求使用的请求体解析器。 ### 3.1.3 JApiResponseResolver 响应解析器,用于指定当前请求使用的响应体解析器。 ### 3.1.4 JBaseUrl 请求地址,通常为域名,例如:`http://www.baidu.com`。 ### 3.1.5 JCookie Cookie - 当注解为类注解,则`required()`无效, 会根据`itemDelimiter()`与`kvDelimiter()`的配置对`value()`进行拆分 - 当注解为方法注解,则`required()`无效, 会根据`itemDelimiter()`与`kvDelimiter()`的配置对`value()`进行拆分 - 当注解为方法参数注解,则`itemDelimiter()`与`kvDelimiter()`的配置无效, 如果方法参数类型为`String`,则`value()`必须配置,对应值为Cookie名称,方法参数为Cookie值; 如果方法参数类型为`Map`,则所有参数无效,方法参数即为Cookie数据 - 注解解析顺序为类注解 -> 方法注解 -> 方法参数注解。同名参数会覆盖 ### 3.1.6 JHeader 请求头 - 当注解为类注解,则`required()`无效, 会根据`itemDelimiter()`与`kvDelimiter()`的配置对`value()`进行拆分 - 当注解为方法注解,则`required()`无效, 会根据`itemDelimiter()`与`kvDelimiter()`的配置对`value()`进行拆分 - 当注解为方法参数注解,则`itemDelimiter()`与`kvDelimiter()`的配置无效, 如果方法参数类型为`String`,则`value()`必须配置,对应值为请求头名称,方法参数为请求头值; 如果方法参数类型为`Map`,则所有参数无效,方法参数即为请求头 - 注解解析顺序为类注解 -> 方法注解 -> 方法参数注解。同名参数会覆盖 ### 3.1.7 JPathParam 路径参数,路径参数形如`{name}` - 如果方法参数类型为`String`,则`value()`必须配置,对应值为路径参数名称,方法参数为路径参数值; 如果方法参数类型为`Map`,则所有参数无效,方法参数即为路径参数数据 ### 3.1.8 JRequestParam 请求参数,可以用在`类`、`方法`或`方法参数`上面。当注解用在`方法参数`上面的时候 - 如果参数为`String`,首先会判断`value`是否为空串,是则注解参数用法相同,不是则`value`为请求参数名称,参数值为对应请求参数的值 - 如果参数为`Collection`或`数组`,则仅有`kvDelimiter`参数有效 - 如果参数为`Map`则所有参数无效 ### 3.1.9 JRequestBody 设置请求体,可以用在`方法参数`上。 ## 3.2 默认响应 为了方便对响应数据的处理,`JHttp`提供了一些默认的操作,在没有主动设置响应对象或响应解析器的情况下,则会根据方法返回值创建响应对象。支持的返回类型以及对应的相应处理类如下: | 返回类型 | 响应类 | | :------------------: | :-----------------: | | java.lang.String | JTextResponse | | byte[] | JByteResponse | | java.nio.ByteBuffer | JByteBufferResponse | | java.io.File | JFileResponse | | org.w3c.dom.Document | JXMLDomResponse | > 需要注意的是响应对象的`getData()`方法的返回值必须与接口方法返回值类型匹配,如果不匹配且返回类型为`JResponse`,则会返回响应对象,否则会永远返回`null` 与请求解析器类似,我们也可以更改默认的响应解析器: ```java apiFactory.getResponseResolverRegistrar().register(responseResolver, true); ``` ## 3.3 插件 接口动态代理的形式简化了我们的开发工作,有些特殊的场景可能需要动态修改参数或拦截请求,这种情况就需要借助插件来完成。下面我们就通过一个小例子演示插件的使用。 ```java @JIntercepts({ @JSignature(type = Weather.class) }) public class Interceptor extends JAbstractInterceptor { @Override public Object intercept(JInvocation invocation) throws Throwable { System.out.println("插件开始工作"); return invocation.proceed(); } } ``` 想要插件生效,我们需要在获得代理对象之前进行设置: ```java apiFactory.addInterceptor(new Interceptor()); ``` 上述插件中演示了插件最基本的用法,编写插件首先需要继承`com.jianggujin.http.support.plugin.JAbstractInterceptor`,或者直接实现`com.jianggujin.http.support.plugin.JInterceptor`接口,在实现的方法中最主要关心`intercept`方法,最后一定不要忘记调用`invocation.proceed()`,否则后续流程会终止。 一个插件可以拦截多个接口代理,这需要依赖`@JIntercepts`与`@JSignature`注解,`@JSignature`注解指定了需要拦截的方法的签名,默认情况在该注解中的`method`属性为空,表示拦截对应接口代理下的所有方法,如果不需要这样做,则需要指定方法名与对应的参数说明以便于定位到指定的方法。 # 第四部分 SpringBoot集成 `JHttp`同样提供了对`SpringBoot`的支持,内置自动装配实现,开发者仅需要关心接口的编写,在需要使用的地方可以直接注入,大大的简化的开发流程。 在不进行任何配置的情况下,想要自动装配扫描并代理我们的接口,需要在接口上添加`@JApiInterface`注解,;另一种形式就是我们将接口放在一个专用的包下面,通过在`SpringBoot`的配置类上面添加`@JApiScan`注解可以更加个性化的对自动装配的接口进行配置。 ```java package com.jianggujin.http.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.jianggujin.http.test.api.Weather; @RunWith(SpringRunner.class) @SpringBootTest public class SpringBootApiTest { @Autowired private Weather weather; @Test public void test() throws Exception { System.err.println(weather.weather("2009", "")); } } ```