JHttp
是用java
编写的用来提供高效的、最新的、功能丰富的支持HTTP
协议的客户端编程工具包,基于原生的HttpUrlConnection
实现,支持:GET
、POST
、PUT
、DELETE
、PATCH
、HEAD
、OPTIONS
、TRACE
请求,可以适应绝大部分业务场景,并提供了丰富的请求操作的API,使开发者更加方便快捷的完成HTTP
的请求工作。
使用JHttp
可以直接下载源代码编译或者下载已经编译的jar
文件,如果您是使用maven
来构建项目,也可以直接在pom.xml
中添加JHttp
的坐标:
<dependency>
<groupId>com.jianggujingroupId>
<artifactId>JHttpartifactId>
<version>最新版本version>
dependency>
最新的版本可以从Maven仓库或者码云获取。
请求文本是最基本的用法,比如下载一个网页的源码或者普通的接口调用,以访问百度为例,我们可以这样写:
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
中还提供了一些常用的操作方法,比如超时时间、字符编码等。
通常情况,我们在网络请求的时候会传递一些请求参数,我们可以使用JRequest
提供的data
方法设置请求参数,该方法有几个重载方法,我们可以按照实际情况选择。
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());
有些时候,我们可能不仅仅是传递普通参数,我们可能希望直接发送一段文本,比如Restful
接口提交JSON
格式数据,使用JHttp
也可以很方便的处理这种需求,在JRequest
中有requestBody(Object body)
方法可以设置要提交的请求体,默认的请求体的处理支持:CharSequence
、Map
、byte[]
和char[]
。
我们可以这样使用:
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)
设置请求指定解析器,这种方式级别最高,如果不设置请求的解析器,请求执行器会尝试查找已经注册的解析器。注册的请求体解析器与请求执行器生命周相同,所以我们可以通过请求执行器获得相关注册器实现注册。
executer.getRequestBodyResolverRegistrar().register(requestBodyResolver, true);
该方法接收两个参数,第一个参数为请求体解析器对象,第二个参数为是否重写,即当出现同名解析器的时候是否替换,如果不替换则会抛出异常。JHttp
提供了一个名为default
的默认的请求体解析器,支持CharSequence
、Map
、ByteBuffer
、byte[]
和char[]
的数据类型转换。
在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
,按照实际需求处理响应。
如果程序运行环境存在网络限制,需要代理访问,我们只需要为请求设置代理对象,方法为proxy(Proxy proxy)
,
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
JRequest.create(url).proxy(proxy );
在处理https
请求的时候,我们可能需要做SSL
的设置,JHttp
提供了JSSLContextFactory
接口用于初始化SSLContext
和HostnameVerifier
,为了使用方便,JHttp
提供了一个JDefaultSSLContextFactory
,我们只需要传递协议算法即可,该类默认忽略证书的验证,并且信任所有主机,为了安全考虑,还是建议实现规范的处理。
如果我们需要做请求的会话保持,使用JHttp
可以很方便的添加Cookie
信息,在JRequest
中,我们提供了几个cookie
的重载方法,方便我们在请求之前添加cookie
信息。
因为JHttp
是基于HttpUrlConnection
的,所以全局的CookieManager
对于JHttp
同样有效。
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());
}
如果我们需要对请求添加日志等处理,我们可以实现com.jianggujin.http.request.JRequestExecuterListener
接口,该接口会在请求执行前与请求之行结束进行相关方法的回调,方便我们做日志记录等处理,在JHttp
中也提供了一个默认的日志实现类com.jianggujin.http.request.impl.JRequestExecuterLog
,当然了,这个类仅仅是做了最简单的打印输出。
executer.setRequestExecuterListener(new JRequestExecuterLog());
这种方式会对请求执行器的所有请求生效,如果想只针对某个请求生效,可以使用如下形式:
executer.execute(request, new JRequestExecuterLog());
JHttp
除了上面的基本使用之外,还支持动态代理方式,通过定义API
接口配合提供的注解,可以更加方便快捷的完成请求处理。使用接口形式,我们首先需要定义请求的接口,举个例子:
@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
获得接口的代理实现:
JApiFactory apiFactory = new JApiFactory();
apiFactory.getApi(Weather.class).weather("2009", "");
接口方法中如果含有JResponse
、Proxy
、JSSLContextFactory
、JRequestBodyResolver
、JMethod
、JKeyVal
类型的参数,会直接将其设置在请求对象中。
设置API请求信息,用于设置请求路径
、方法
等信息,可以用在方法
上面。与JBaseUrl
搭配使用,会将域名与路径进行拼接。
请求体解析器,用于指定当前请求使用的请求体解析器。
响应解析器,用于指定当前请求使用的响应体解析器。
请求地址,通常为域名,例如:http://www.baidu.com
。
Cookie
当注解为类注解,则required()
无效, 会根据itemDelimiter()
与kvDelimiter()
的配置对value()
进行拆分
当注解为方法注解,则required()
无效, 会根据itemDelimiter()
与kvDelimiter()
的配置对value()
进行拆分
当注解为方法参数注解,则itemDelimiter()
与kvDelimiter()
的配置无效, 如果方法参数类型为String
,则value()
必须配置,对应值为Cookie名称,方法参数为Cookie值; 如果方法参数类型为Map
,则所有参数无效,方法参数即为Cookie数据
注解解析顺序为类注解 -> 方法注解 -> 方法参数注解。同名参数会覆盖
请求头
当注解为类注解,则required()
无效, 会根据itemDelimiter()
与kvDelimiter()
的配置对value()
进行拆分
当注解为方法注解,则required()
无效, 会根据itemDelimiter()
与kvDelimiter()
的配置对value()
进行拆分
当注解为方法参数注解,则itemDelimiter()
与kvDelimiter()
的配置无效, 如果方法参数类型为String
,则value()
必须配置,对应值为请求头名称,方法参数为请求头值; 如果方法参数类型为Map
,则所有参数无效,方法参数即为请求头
注解解析顺序为类注解 -> 方法注解 -> 方法参数注解。同名参数会覆盖
路径参数,路径参数形如{name}
String
,则value()
必须配置,对应值为路径参数名称,方法参数为路径参数值; 如果方法参数类型为Map
,则所有参数无效,方法参数即为路径参数数据请求参数,可以用在类
、方法
或方法参数
上面。当注解用在方法参数
上面的时候
String
,首先会判断value
是否为空串,是则注解参数用法相同,不是则value
为请求参数名称,参数值为对应请求参数的值Collection
或数组
,则仅有kvDelimiter
参数有效Map
则所有参数无效设置请求体,可以用在方法参数
上。
为了方便对响应数据的处理,JHttp
提供了一些默认的操作,在没有主动设置响应对象或响应解析器的情况下,则会根据方法返回值创建响应对象。支持的返回类型以及对应的相应处理类如下:
返回类型 | 响应类 |
---|---|
java.lang.String | JTextResponse |
byte[] | JByteResponse |
java.nio.ByteBuffer | JByteBufferResponse |
java.io.File | JFileResponse |
org.w3c.dom.Document | JXMLDomResponse |
需要注意的是响应对象的
getData()
方法的返回值必须与接口方法返回值类型匹配,如果不匹配且返回类型为JResponse
,则会返回响应对象,否则会永远返回null
与请求解析器类似,我们也可以更改默认的响应解析器:
apiFactory.getResponseResolverRegistrar().register(responseResolver, true);
接口动态代理的形式简化了我们的开发工作,有些特殊的场景可能需要动态修改参数或拦截请求,这种情况就需要借助插件来完成。下面我们就通过一个小例子演示插件的使用。
@JIntercepts({ @JSignature(type = Weather.class) })
public class Interceptor extends JAbstractInterceptor {
@Override
public Object intercept(JInvocation invocation) throws Throwable {
System.out.println("插件开始工作");
return invocation.proceed();
}
}
想要插件生效,我们需要在获得代理对象之前进行设置:
apiFactory.addInterceptor(new Interceptor());
上述插件中演示了插件最基本的用法,编写插件首先需要继承com.jianggujin.http.support.plugin.JAbstractInterceptor
,或者直接实现com.jianggujin.http.support.plugin.JInterceptor
接口,在实现的方法中最主要关心intercept
方法,最后一定不要忘记调用invocation.proceed()
,否则后续流程会终止。
一个插件可以拦截多个接口代理,这需要依赖@JIntercepts
与@JSignature
注解,@JSignature
注解指定了需要拦截的方法的签名,默认情况在该注解中的method
属性为空,表示拦截对应接口代理下的所有方法,如果不需要这样做,则需要指定方法名与对应的参数说明以便于定位到指定的方法。
JHttp
同样提供了对SpringBoot
的支持,内置自动装配实现,开发者仅需要关心接口的编写,在需要使用的地方可以直接注入,大大的简化的开发流程。
在不进行任何配置的情况下,想要自动装配扫描并代理我们的接口,需要在接口上添加@JApiInterface
注解,;另一种形式就是我们将接口放在一个专用的包下面,通过在SpringBoot
的配置类上面添加@JApiScan
注解可以更加个性化的对自动装配的接口进行配置。
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", ""));
}
}