项目介绍
Forest是一个高层的、极简的声明式HTTP调用API框架。相比于直接使用Httpclient您不再用写一大堆重复的代码了,而是像调用本地方法一样去发送HTTP请求。
Maven依赖
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.19</version>
</dependency>
快速上手
简单请求
public interface MyClient {
@Request("http://localhost:8080/hello")
String simpleRequest();
}
通过@Request注解,将上面的MyClient接口中的simpleRequest()方法绑定了一个 HTTP 请求, 其 URL 为http://localhost:8080/hello ,并默认使用GET方式,且将请求响应的数据以String的方式返回给调用者。
稍微复杂点的请求,需要在请求头设置信息
public interface MyClient {
@Request(
url = "http://localhost:8080/hello/user",
headers = "Accept: text/plain"
)
String sendRequest(@Query("uname") String username);
}
上面的sendRequest方法绑定的 HTTP 请求,定义了 URL 信息,以及把Accept:text/plain加到了请求头中, 方法的参数String username绑定了注解@Query("uname"),它的作用是将调用者传入入参 username 时,自动将username的值加入到 HTTP 的请求参数uname中。
这段实际产生的HTTP请求如下:
GET http://localhost:8080/hello/user?uname=foo
HEADER:
Accept: text/plain
请求方法,假设发起post请求,有3种写法:
public interface MyClient {
/**
* 使用 @Post 注解,可以去掉 type = "POST" 这行属性
*/
@Post("http://localhost:8080/hello")
String simplePost1();
/**
* 通过 @Request 注解的 type 参数指定 HTTP 请求的方式。
*/
@Request(
url = "http://localhost:8080/hello",
type = "POST"
)
String simplePost2();
/**
* 使用 @PostRequest 注解,和上面效果等价
*/
@PostRequest("http://localhost:8080/hello")
String simplePost3();
}
可以用@GetRequest, @PostRequest等注解代替@Request注解,这样就可以省去写type属性的麻烦了。
请求体
在POST和PUT等请求方法中,通常使用 HTTP 请求体进行传输数据。在 Forest 中有多种方式设置请求体数据。
表单格式
上面使用 @Body 注解的例子用的是普通的表单格式,也就是contentType属性为application/x-www-form-urlencoded的格式,即contentType不做配置时的默认值。
表单格式的请求体以字符串 key1=value1&key2=value2&...&key{n}=value{n} 的形式进行传输数据,其中value都是已经过 URL Encode 编码过的字符串。
/**
* contentType属性设置为 application/x-www-form-urlencoded 即为表单格式,
* 当然不设置的时候默认值也为 application/x-www-form-urlencoded, 也同样是表单格式。
* 在 @Body 注解的 value 属性中设置的名称为表单项的 key 名,
* 而注解所修饰的参数值即为表单项的值,它可以为任何类型,不过最终都会转换为字符串进行传输。
*/
@Post(
url = "http://localhost:8080/user",
contentType = "application/x-www-form-urlencoded",
headers = {"Accept:text/plain"}
)
String sendPost(@Body("key1") String value1, @Body("key2") Integer value2, @Body("key3") Long value3);
调用后产生的结果可能如下:
POST http://localhost:8080/hello/user
HEADER:
Content-Type: application/x-www-form-urlencoded
BODY:
key1=xxx&key2=1000&key3=9999
当@Body注解修饰的参数为一个对象,并注解的value属性不设置任何名称的时候,会将注解所修饰参数值对象视为一整个表单,其对象中的所有属性将按 属性名1=属性值1&属性名2=属性值2&...&属性名{n}=属性值{n} 的形式通过请求体进行传输数据。
/**
* contentType 属性不设置默认为 application/x-www-form-urlencoded
* 要以对象作为表达传输项时,其 @Body 注解的 value 名称不能设置
*/
@Post(
url = "http://localhost:8080/hello/user",
headers = {"Accept:text/plain"}
)
String send(@Body User user);
调用产生的结果如下:
POST http://localhost:8080/hello/user
HEADER:
Content-Type: application/x-www-form-urlencoded
BODY:
username=foo&password=bar
JSON格式
@JSONBody注解修饰对象
@JSONBody = @Body+contentType的格式,除了@JSONBody注解,使用@Body注解也可以,只要将contentType属性或Content-Type请求头指定为application/json便可。
发送JSON非常简单,只要用@JSONBody注解修饰相关参数就可以了,该注解自1.5.0-RC1版本起可以使用。 使用@JSONBody注解的同时就可以省略 contentType = "application/json"属性设置
/**
* 被@JSONBody注解修饰的参数会根据其类型被自定解析为JSON字符串
* 使用@JSONBody注解时可以省略 contentType = "application/json"属性设置
*/
@Post("http://localhost:8080/hello/user")
String helloUser(@JSONBody User user);
调用后产生的结果如下:
POST http://localhost:8080/hello/user
HEADER:
Content-Type: application/json
BODY:
{"username": "foo", "password": "bar"}
切记使用@JSONBody绑定对象入参的时候,JSONBody的value一定要空着,比如,@JSONBody User user写法:
/**
* 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
*/
@Post(url = "http://localhost:8080/hello/user")
String helloUser1(@JSONBody User user);
当@JSONBody修饰Map的时候:
/**
* 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
*/
@Post(url = "http://localhost:8080/hello/user")
String helloUser2(@JSONBody Map<String, Object> userMap);
//若调用代码是这样的:
Map<String, Object> map = new HashMap<>();
map.put("username", "foo");
map.put("password", "bar");
client.helloUser(map);
//会产生的结果:
POST http://localhost:8080/hello/user
HEADER:
Content-Type: application/json
BODY:
{"username": "foo", "password": "bar"}
详细forest请求体说明可以参考官方文档:forest.dtflyx.com/docs/basic/…
注解BaseRequest
@BaseRequest注解定义在接口类上,在@BaseRequest上定义的属性会被分配到该接口中每一个方法上,但方法上定义的请求属性会覆盖@BaseRequest上重复定义的内容。 因此可以认为@BaseRequest上定义的属性内容是所在接口中所有请求的默认属性。
/**
* @BaseRequest 为配置接口层级请求信息的注解
* 其属性会成为该接口下所有请求的默认属性
* 但可以被方法上定义的属性所覆盖
*/
@BaseRequest(
baseURL = "http://localhost:8080", // 默认域名
headers = {
"Accept:text/plain" // 默认请求头
},
sslProtocol = "TLS" // 默认单向SSL协议
)
public interface MyClient {
// 方法的URL不必再写域名部分
@Get("/hello/user")
String send1(@Query("username") String username);
// 若方法的URL是完整包含http://开头的,那么会以方法的URL中域名为准,不会被接口层级中的baseURL属性覆盖,这个确实是非常方便了~
@Get("http://www.xxx.com/hello/user")
String send2(@Query("username") String username);
@Get(
url = "/hello/user",
headers = {
"Accept:application/json" // 覆盖接口层级配置的请求头信息
}
)
String send3(@Query("username") String username);
}
forest异步请求
在Forest使用异步请求,可以通过设置@Request注解的async属性为true实现,不设置或设置为false即为同步请求
/**
* async 属性为 true 即为异步请求,为 false 则为同步请求
* 不设置该属性时,默认为 false
*/
@Post(
url = "http://localhost:8080/user/updateUserTagById",
async = true
)
boolean asyncUpdate(String userId);