Skip to main content
Version: 1.5.x

请求对象

Forest请求对象,即 ForestRequest 类对象是Forest框架中的核心组件之一。所有的Forest接口方法在调用后都会转化为 ForestRequest 对象,然后再调用后端的底层框架(HttpClient或OkHttp3)执行实际的请求发送过程。

友情提示

本文档中 Forest 请求对象的接口操作都基于 1.5.3 及以上版本

构建请求#

在Forest中有两种创建 ForestRequest 对象的方法:接口方法返回请求对象和快捷接口创建请求对象。

我们先来说第一中方法,即通过自定义的Forest请求接口获取请求对象。

先在请求接口定义的方法中,使用 ForestRequest 作为方法返回类型。再调用该方法后会直接返回 ForestRequest 对象,而不会执行发送请求的过程。


public interface MyForestClient {
    /**     * Get类型请求,url路径为 /test     * <p>ForestRequest是带泛型参数的类型     * <p>泛型参数代表返回的响应数据所期望转化成的类型     *      * @return Forest请求对象     */    @Get("/test")    ForestRequest<?> getForestRequest();}

调用 getForestRequest() 方法后即可获取该方法所对应的 Forest 请求对象。

@ResouceMyForestClient myForestClient;
... ...        // 如果请求方法以 ForestRequest 作为返回值类型// 不会直接发送请求// 而是直接返回 Forest 请求对象        ForestRequest<?> request = myForestClient.getForestRequest();String path = request.path(); // 得到字符串 /test// 手动执行发送请求,并以字符串形式接受响应数据String ret = request.execute(String.class);
友情提示

若请求接口的方法以 ForestRequest 类作为返回值类型,调用该方法时不会直接执行发送请求的过程,而是返回 ForestRequest 类型的对象实例,以便做进一步的操作。 得到 Forest 请求对象后需要手动调用执行方法进行请求发送的过程。 关于如何手动发送请求,请求参见《执行请求

第二种办法更为简单,即直接通过Forest快捷接口创建请求对象

// 通过ID为 ForestConfiguration Id 的Forest配置来创建一个新的 Forest 请求对象ForestRequest<?> request1 = Forest.config("ForestConfiguration Id").request();// 通过默认全局配置来创建一个新的 Forest 请求对象ForestRequest<?> request2 = Forest.config().request();// 同上,即通过默认全局配置来创建一个新的 Forest 请求对象 (推荐使用)ForestRequest<?> request3 = Forest.request();

Forest.request() 会创建一个包含默认请求属性的ForestRequest对象

Forest的快捷接口还提供了其它快速创建请求对象的方法,可以在创建时就指定请求类型和url地址路径

// 创建 GET 请求对象Forest.get("/");// 创建 POST 请求对象Forest.post("/");// 创建 PUT 请求对象Forest.put("/");// 创建 DELETE 请求对象Forest.delete("/");// 创建 HEAD 请求对象Forest.head("/");// 创建 PATCH 请求对象Forest.patch("/");// 创建 OPTIONS 请求对象Forest.options("/");// 创建 TRACE 请求对象Forest.trace("/");

以上示例代码中的方法名即为请求类型,参数为url地址,如: get("/") 就会返回 GET 类型请求,同时url地址为 http://localhost/

此类方法的参数可以接受一个简短的不包含域名/端口号部分的路径字符串作为参数,也可以接受完整的url地址

// 参数只传 path 部分// 请求地址为 http://localhost/testForest.get("/test")
// 参数传如完整的 URL 地址// 请求地址同上,也为 http://localhost/testForest.get("http://localhost/test")

获取请求#

在拦截器或 OnError、OnSuccess 等回调函数的参数中可以直接获取 Forest 请求对象,相关内容请参见《拦截器

下面将接受 ForestRequest 对象各个属性的获取和修改的方法,以及如何执行发送请求的过程。

文档提示

在后面的示例代码中,ForestRequest 对象都将以 request 这一变量名表示。

请求属性#

Forest请求对象的基本属性和默认值如下表:

属性名描述默认值
type请求类型GET
schemeHTTP协议http
path请求地址路径/
host请求主机地址localhost
port请求地址端口-1 (代表没有端口号)
urlURL地址http://localhost/
charset请求参数的编码字符集UTF-8
async是否异步false
autoRedirection是否自动重定向true
timeout请求超时时间3000 (毫秒)
decompressResponseGzipEnabled是否开启解压GZIP响应内容false
maxRetryCount最大请求重试次数0
maxRetryInterval最大请重试的时间间隔0 (毫秒)
retryer请求重试器BackOffRetryer (基于退避算法的重试器)

执行请求#

ForestRequest 对象可以直接调用 execute() 方法执行请求,即发送请求到远端服务器

// 发送 Get 请求, url: http://localhost/Forest.get("/").execute();// execute 方法会返回服务端响应的数据Object ret = Forest.post("/").execute();

Forest请求对象也提供多个方法,以满足用不同类型接受数据的需要

自定义类型#

execute(Class<R> clazz) 执行请求发送过程

  • 参数clazz: 结果返回类型, Class 对象
// 以字符串方式接受数据String str = Forest.get("/").execute(String.class);// 以自定义类型方式接受数据MyResult result = Forest.get("/").execute(MyResult.class);

Type类型#

execute(Type type) 执行请求发送过程

  • 参数type: 结果返回类型, Type 接口实例
// 使用 TypeReference 工具类包裹泛型类型TypeReference<List<String>> typeRef = new TypeReference() {};// 获取 Type 对象Type type = typeRef.getType();// 以自定义Type类型形式接受数据List<String> strList = Forest.get("/").execute(type);

泛型类型#

execute(TypeReference typeReference) 执行请求发送过程

  • 参数typeReference: 结果返回类型的引用, Type 接口实例
// 使用 TypeReference 工具类包裹泛型类型TypeReference<List<String>> typeRef = new TypeReference() {};// 以带复杂泛型参数的类型形式接受数据List<String> strList = Forest.get("/").execute(typeRef);

字符串类型#

executeAsString() 执行请求发送过程,并获取字符串类型结果

// 直接以 String 类型接受数据String str = Forest.get("/").executeAsString();

列表类型#

executeAsList() 执行请求发送过程,并获取List类型结果

// 直接以 List 类型接受数据List<String> list = Forest.get("/").executeAsList();

Map类型#

executeAsMap() 执行请求发送过程,并获取Map类型结果

// 直接以 Map 类型接受数据Map<String, String> map = Forest.get("/").executeAsMap();

后端框架#

ForestRequest 对象可以设置/获取该请求所对应的后端框架

// 将后端HTTP框架设置为 httpclientrequest.backend("httpclient");// 将后端HTTP框架设置为 okhttp3request.backend("okhttp3");// 获取当前请求所对应的后端HTTP框架requet.backend();

请求类型#

ForestRequest 对象提供了设置/获取请求类型的方法

// 设置请求类型为 GET, 参数为 ForestRequestType 枚举类型request.setType(ForestRequestType.GET);// 获取当前的请求类型,返回 ForestRequestType 枚举request.getType();

从第二次开始后修改请求类型,就会记录上一次的请求类型的变更历史

// 创建新的 Forest 请求对象ForestRequest<?> request = Forest.get("/");// 第一次设置为 GET 请求request.setType(ForestRequestType.GET);// 第二次设置为 POST 请求request.setType(ForestRequestType.POST);

在发送请求时会在日志中打印出请求类型的变更历史

[Forest] Request :     [Type Change]: GET -> POST    POST http://localhost/ HTTP

URL地址#

ForestRequest 对象提供设置/获取URL地址的接口方法

url(String url) 设置请求URL

  • 参数 url: URL字符串

url(ForestURL url) 设置请求URL

  • 参数 url: ForestURL 类实例

host(String host) 设置URL的主机名/域名/IP地址部分

  • 参数 host: 主机名/域名/IP地址

port(int port) 设置URL中的端口

  • 参数 port: 端口号

scheme(String scheme) 设置URL中的HTTP协议头

  • 参数 scheme: HTTP协议头

path(String path) 设置URL路径

  • 参数 path: URL路径

ref(String ref) 设置Reference, 即URL井号后面的字符串

  • 参数 ref: URL井号后面的字符串
// 设置请求URL地址,url: http://127.0.0.1:8080/testrequest.url("http://127.0.0.1:8080/test");// 设置路径为 /abc, 主机地址和端口部分保留,url: http://127.0.0.1:8080/abcrequest.url("/abc");// 覆盖掉整个URL地址,url: http://forest.dtflyx.com/111request.url("http://forest.dtflyx.com/111");// 使用 ForestURL 类型对象作为参数,url: http://localhost:8080/222request.url(new ForestURL(new URL("http://localhost:8080/222")));// 修改请求地址中的主机名/IP地址/域名部分,url: http://127.0.0.1:8080/222request.host("127.0.0.1");// 同上,修改请求地址中的主机名/IP地址/域名部分request.url().setHost("127.0.0.1");// 修改请求地址中的端口号,url: http://127.0.0.1:8888/222request.port(8888);// 同上,修改请求地址中的端口号request.url().setPort(8888);// 修改请求地址中的HTTP协议头,url: https://127.0.0.1:8888/222request.scheme("https");// 同上,修改请求地址中的HTTP协议头request.url().setScheme("https");// 修改请求地址中的路径部分,和url方法类似,但path方法不接受完整url地址作为参数,url: https://127.0.0.1:8888/333request.path("/333");// 同上,修改请求地址中的路径部分request.url().setPath("/333");// 设置Reference,即URL井号后面的字符串,url: https://127.0.0.1:8888/333#hellorequest.ref("hello");

获取URL地址

urlString() 获取请求URL字符串

  • 返回值: URL字符串

url() 获取请求URL

  • 返回值: ForestURL 类实例

host() 获取URL中的主机名/域名/IP地址部分

  • 返回值: 主机名/域名/IP地址部分字符串

port() 获取URL中的端口号

  • 返回值: int 整数, URL中的端口号

path() 获取URL路径

  • 返回值: URL路径

ref() 获取Reference, 即URL井号后面的字符串

  • 返回值: URL井号后面的字符串
// 获取URL地址字符串// 如:http://localhost:8080/testrequest.urlString();// 获取ForestURL类对象request.url();// 获取请求地址的HTTP协议头request.scheme();// 同上,获取请求地址的HTTP协议头request.url().getScheme();// 获取请求地址中的主机名/IP地址/域名部分request.host();// 同上,获取请求地址中的主机名/IP地址/域名部分request.url().getHost();// 获取请求地址中的端口号// 如:8080// 默认为 -1, 即没有端口号// 没有端口号也可以理解为 http 下的 80 和 https 下的 443request.port();// 同上,获取请求地址中的端口号request.url().getPort();// 获取请求地址中的路径部分,如:/testrequest.path();// 同上,获取请求地址中的路径部分request.url().getPath();// 获取Reference,即URL井号后面的字符串// 如:http://localhost/test#hello 中的 hello 那部分字符串request.ref(); 

主机地址#

主机地址包含: 主机名/IP地址/域名 + 端口号

使用 address 方法的好处是,只会修改主机名或端口,不会影响URL的其它部分(比如 Query 参数,HTTP协议头,路径部分等等)

address(String host, int port) 设置请求的主机地址(主机名/域名/IP地址 + 端口号)

  • 参数 host: 主机名/域名/IP地址
  • 参数 port: 端口号
// 创建新的请求对象// url: http://localhost/testForest.get("/test")     // 设置请求主机地址,不会改变URL地址的其它部分     // 参数1:主机名/IP地址/域名     // 参数2:端口号     // url: http://192.168.0.1:8881/test    .address("192.168.0.1", 8881)     // 同上,设置请求主机地址     // 参数为 ForestAddress 对象     // url: http://192.168.0.2:8882/test    .address(new ForestAddress("192.168.0.2", 8882));

URL参数#

URL参数,也称为Query (查询参数),是跟随在URL地址中 ? 后的字符串,常以 key=value 的键值对形式出现,用于向服务端传递数据。

ForestRequest 对象提供了添加和修改 Query 参数的方法

添加参数#

addQuery(String name, Object value) 添加Query参数

  • 参数 name: Query参数名
  • 参数 value: Query参数值
Forest.get("/")        .addQuery("a", 1)  // 添加 Query 参数 a=1        .addQuery("b", 2); // 添加 Query 参数 b=2// 最后产生 Query 参数 ?a=1&b=2

适用 ForestRequest.addQuery 方法可以重复添加重名 Query 参数。 此功能可用于传递数组


request.addQuery("a", 1)  // 添加 Query 参数 a=1       .addQuery("a", 2)  // 添加 Query 参数 a=2       .addQuery("a", 3); // 添加 Query 参数 a=3// 最后产生 Query 参数 ?a=1&a=2&a=3

强制编码#

注意

使用 addQuery(String name, String value) 方法时, 绝大部分Query参数值都会被自动UrlEncode编码,但有某些特殊的参数值不会被编码,如: http://www.baidu.com 这样的url地址会被原封不动保留。

这时候需要使用 addQuery(String name, String value, boolean isUrlEncode, String charset) 方法来强制UrlEncode。

addQuery(String name, String value, boolean isUrlEncode, String charset) 添加Query参数

  • 参数 name: Query参数名
  • 参数 value: Query参数值
  • 参数 isUrlEncode: 是否强制UrlEncode
  • 参数 charset: 编码字符集
Forest.get("http://127.0.0.1:8080/")         // 不强制UrlEncode        .addQuery("url1", "http://localhost/test");         // 强制UrlEncode        .addQuery("url2", "http://localhost/test", true, "UTF-8");// 最终url为// http://127.0.0.1:8080/?url1=http://localhost/test&url2=http%3A%2F%2Flocalhost%2Ftest

修改参数#

修改已存在的 Query 参数

replaceQuery(String name, Object value) 替换Query参数值

  • 参数 name: Query参数名
  • 参数 value: Query参数值
request.addQuery("a", 1)      // 添加 Query 参数 a=1       .replaceQuery("a", 2); // 修改 Query 参数 a=2// 最后产生 Query 参数 ?a=2

ForestRequest.replaceQuery 可以方法修改 Query 参数,但对于还不存在的 Query 参数就无法处理了

// 创建一个新请求对象// url: http://localhost/Forest.get("/")       // 这时候还不存在 Query 参数 a       // 所以此时调用 replaceQuery 是无效的      .replaceQuery("a", "1");// 最后地址还是为 http://localhost/

这时候可以用 ForestRequest.replaceOrAddQuery 方法,它在 Query 参数不存在的情况下添加参数,在已存在的情况下修改参数

replaceOrAddQuery(String name, String value) 替换或添加Query参数

  • 参数 name: Query参数名
  • 参数 value: Query参数值
// 创建一个新请求对象// url: http://localhost/Forest.get("/");      .replaceOrAddQuery("a", "1")  // 添加或修改 Query 参数 a=1      .addQuery("b", "2")           // 修改 Query 参数 a=2      .replaceOrAddQuery("b", "3"); // 添加或修改 Query 参数 b=3// 最后地址还是为 http://localhost/?a=1&b=3

数组参数#

有些时候,需要通过URL参数传递一个数组或者一个列表

// 添加列表到Query参数List<Integer> list = Arrays.asList(1, 2, 3);Forest.get("/")     .addQuery("a", list)     .execute();// query参数为 a=1&a=2&a=3
// 添加数组到Query参数Object[] array = new Object[] {1, 2, 3};Forest.get("/")     .addQuery("a", array)     .execute();// Query参数为 a=1&a=2&a=3
// 添加数组到Query参数 (可变参数)Forest.get("/")     .addQuery("a", 1, 2, 3)     .execute();// Query参数为 a=1&a=2&a=3

带方括号[]参数名的样式

// 添加列表到Query参数List<Integer> list = Arrays.asList(1, 2, 3);Forest.get("/")     .addArrayQuery("a", list)     .execute();// query参数为 a[]=1&a[]=2&a[]=3
// 添加数组到Query参数Object[] array = new Object[] {1, 2, 3};Forest.get("/")     .addArrayQuery("a", array)     .execute();// Query参数为 a[]=1&a[]=2&a[]=3
// 添加数组到Query参数 (可变参数)Forest.get("/")     .addArrayQuery("a", 1, 2, 3)     .execute();// Query参数为 a[]=1&a[]=2&a[]=3

JSON参数#

如果不想以URL参数的标准格式传递列表,JSON字符串也是一种选择

addJSONQuery(String name, Object value) 添加 JSON Query 参数

  • 参数name: Query参数名
  • 参数value: Query参数值,将被序列化为JSON字符串
// 添加列表到Query数组参数Forest.get("/")     .addJSONQuery("a", Arrays.asList(1, 2, 3))     .execute();// query参数为 a=[1,2,3]// 注意:这里的JSON数据最终会被 URLEncode// 所以最终请求的参数为 a=%5B1%2C2%2C3%5D

获取参数#

获取请求的整个 Query 参数表

getQuery() 获取请求的Query参数表

  • 返回值: Query参数表,ForestQueryMap 类实例
// 构建请求// Query 参数为 a=1&b=2&b=3ForestRequest<?> request = Forest.get("/")        .addQuery("a", 1)        .addQuery("b", 2)        .addQuery("b", 3);// 获取 Query 参数表ForestQueryMap queries = request.getQuery();// 返回参数为 a=1 的 ForestQueryParameter 对象ForestQueryParameter param1 = queries.getQuery("a");// 返回包含 b=2和b=3 的 ForestQueryParameter 对象列表List<ForestQueryParameter> params = queries.getQueries("b"); 

获取单个 Query 参数

getQuery(String name) 根据名称获取请求的Query参数值

  • 参数name: Query参数名称
  • 返回值: Query参数值
// 构建请求// Query 参数为 a=1&b=2&b=3ForestRequest<?> request = Forest.get("/")        .addQuery("a", 1)        .addQuery("b", 2);
request.getQuery("a"); // 获取 1request.getQuery("b"); // 获取 2

获取整个请求的 Query 参数字符串

getQueryString() 获取请求的URL Query参数字符串

  • 返回值: Query参数字符串
// 构建请求// Query 参数为 a=1&b=2&b=3ForestRequest<?> request = Forest.get("/")        .addQuery("a", 1)        .addQuery("b", 2)        .addQuery("c", 3);
request.getQueryString(); // 获取 a=1&b=2&c=3

请求头#

一个HTTP请求由请求行、请求头、请求体三部分组成。其中,请求头是HTTP请求的重要组成部分,经常用于传递和服务端通讯的控制参数、数据格式、请求编码等重要信息。

请求头由零到多个头属性构成,而一个头属性通常是一个键值对。

ForestRequest 对象提供了添加和修改请求头的方法

addHeader(String name, Object value) 添加请求头

  • 参数 name: 请求头名称

  • 参数 value: 请求头的值

// addHeader方法第一个参数为头属性名称,第二个参数为头属性的值// 添加头属性 Content-Type,值为 application/jsonrequest.addHeader("Content-Type", "application/json");

一个请求可以添加多个头属性

request.addHeader("Accept", "text/plain")              // 添加第一个头属性       .addHeader("Content-Type", "application/json")  // 添加第二个头属性       .addHeader("AccessToken", "foobar");            // 添加第三个头属性
// 最终请求包含以下头属性:// Accept: text/plain// Content-Type: application/json// AccessToken: foobar

修改和覆盖一个已存在的头属性

request.addHeader("User-Agent", "httpclient") // 添加第一个头属性       .addHeader("User-Agent", "forest");    // 覆盖第一个头属性// 最终请求包含以下头属性:// User-Agent: forest

获取请求头

header(String name) 根据请求头名称获取请求头

  • 参数 name: 请求头名称
  • 返回值: 请求头,ForestHeader 类实例

headers() 获取所有请求头

  • 返回值: 请求头表, ForestHeaderMap 类实例
// 创建新的请求对象// url: http://localhost/testForestRequest<?> request = Forest.get("/")        .addHeader("Accept", "text/plain")              // 添加第一个头属性        .addHeader("Content-Type", "application/json")  // 添加第二个头属性        // 通过 header 方法可以根据头属性名获取请求头对象// 并返回 ForestHeader 类的对象        ForestHeader header1 = request.header("Accept");        // 获取头属性 AcceptForestHeader header2 = request.header("Content-Type");  // 获取头属性 Content-Type// 通过 headers 方法可以获取该请求的所有请求头信息// 并返回 ForestHeaderMap 类对象ForestHeaderMap headerMap = request.headers();          // 获取请求的所有请求头// 通过 headerValue 方法可以根据头属性名获取请求头的属性值// 并返回属性值字符串String headerValue1 = request.headerValue("Accept");          // 获取头属性 Accept 的属性值String headerValue2 = request.headerValue("Content-Type");    // 获取头属性 Accept 的属性值

特殊请求头#

Forest为一些特殊头属性定义专有的方法,不用在 addHeader 方法中传入头属性名了,比如: Content-Type

contentType(String contentType) 设置请求头 Content-Type 的值

  • 参数 contentType: 请求头 Content-Type 的值
// 设置头属性 Content-Type 的值为 application/jsonrequest.contentType("application/json");

Forest也为 Content-Type 常用属性值的设置提供了快捷方法

// 设置头属性 Content-Type: application/x-www-form-urlencodedrequest.contentFormUrlEncoded();// 设置头属性 Content-Type: application/jsonrequest.contentTypeJson();// 设置头属性 Content-Type: application/xmlrequest.contentTypeXml();// 设置头属性 Content-Type: multipart/form-datarequest.contentTypeMultipartFormData();// 设置头属性 Content-Type: application/octet-streamrequest.contentTypeOctetStream();

请求体#

ForestRequest 对象提供的添加/修改请求体数据项的方法

addBody(String bodyString) 添加字符串Body数据项

request.addBody("xxxx")   // 添加字符串请求体 xxxx       .addBody("yyyy");  // 添加字符串请求体 yyyy// 最终请求体为:// xxxxyyyy

addBody(String name, Object value) 添加键值对形式的Body数据项

request.addBody("a", 1)  // 添加请求体数据项,键值对: a, 1       .addBody("b", 2); // 添加请求体数据项,键值对: b, 2

现在 ForestRequest 对象中有了两个请求体数据项,风别是键值对<a, 1><b, 2> 至于它们最终以什么数据格式发送,取决于请求头中的 Content-Type 的值

addBody(Object obj) 添加对象形式的Body数据项

  • 参数 obj: 作为请求体数据的任意类型对象
// 添加自定义类对象MyUserInfo userInfo = new MyUserInfo();userInfo.setName("foo");request.addBody(userInfo);
// 添加Map对象Map<String, Object> map = new HashMap<>();map.put("name", "foo");map.put("value", "bar");request.addBody(map);

表单数据#

在调用 addBody 方法前,需要先指定 Content-Type 为 application/x-www-form-urlencoded

插入键值对形式的表单数据

// 创建表格格式请求体// 按键值对形式添加请求体数据项Forest.post("/")    .contentFormUrlEncoded()  // 指定请求体为表单格式    .addBody("name", "foo")   // 添加Body键值对: name, foo    .addBody("value", "bar"); // 添加Body键值对: value, bar// 最终请求体数据为// name=foo&value=bar

插入Map形式的表单数据

// 构建Map对象Map<String, Object> map = new HashMap();map.put("name", "foo");map.put("value" "bar");// 创建表格格式请求体// 按Map形式添加请求体数据项Forest.post("/")    .contentFormUrlEncoded() // 指定请求体为表单格式    .addBody(map);           // 添加Map对象到请求体中// 最终请求体数据为// name=foo&value=bar

插入自定义对象形式的表单数据

// 用自定义的 MyUserInfo 类对象MyUserInfo user = new MyUserInfo();user.setUsername("Jack");user.setAge(20);// 创建表格格式请求体// 按自定义对象形式添加请求体数据项Forest.post("/")    .contentFormUrlEncoded()  // 指定请求体为表单格式    .addBody(user);           // 添加自定义类对象到请求体中// 最终请求体数据为// username=Jack&age=20

混合使用多种形式

// 构建Map对象Map<String, Object> map = new HashMap();map.put("name", "foo");map.put("value" "bar");// 构建自定义类对象MyUserInfo user = new MyUserInfo();user.setUsername("Jack");user.setAge(20);// 创建表格格式请求体// 按自定义对象形式添加请求体数据项Forest.post("/")    .contentFormUrlEncoded() // 指定请求体为表单格式    .addBody("a", 1)         // 添加Body键值对: a, 1    .addBody("b", 2)         // 添加Body键值对: b, 2    .addBody(map)            // 添加Map对象到请求体中    .addBody(user);          // 添加自定义类对象到请求体中// 最终请求体数据为// a=1&b=2&name=foo&value=bar&username=Jack&age=20

JSON数据#

在调用 addBody 方法前,需要先指定 Content-Type 为 application/json 或其他json字符串结尾的 Content-Type 值

插入键值对形式的JSON数据

// 创建JSON格式请求体// 按键值对形式添加请求体数据项Forest.post("/")    .contentTypeJson()        // 指定请求体为JSON格式    .addBody("name", "foo")   // 添加Body键值对: name, foo    .addBody("value", "bar"); // 添加Body键值对: value, bar// 最终请求体数据为// {"name": "foo", "value": "bar"}

插入Map或List形式的表单数据

// 构建Map对象Map<String, Object> map = new HashMap();map.put("name", "foo");map.put("value" "bar");// 创建JSON格式请求体// 按Map形式添加请求体数据项Forest.post("/")    .contentTypeJson() // 指定请求体为JSON格式    .addBody(map);     // 添加Map对象到请求体中// 最终请求体数据为// {"name": "foo", "value": "bar"}
// 构建List对象List<Integer> list = new ArrayList();value.add(1);value.add(2);value.add(3);// 创建JSON格式请求体// 按Map形式添加请求体数据项Forest.post("/")    .contentTypeJson() // 指定请求体为JSON格式    .addBody(list);    // 添加List对象到请求体中// 最终请求体数据为// [1, 2, 3]

插入自定义对象形式的表单数据

// 构建自定义类对象MyUserInfo user = new MyUserInfo();user.setUsername("Jack");user.setAge(20);// 创建JSON格式请求体// 按自定义对象形式添加请求体数据项Forest.post("/")    .contentTypeJson()  // 指定请求体为JSON格式    .addBody(user);     // 添加自定义类对象到请求体中// 最终请求体数据为// {"username": "Jack", "age", 20}

JSON数据请求体,同样可以混合多种形式添加

// 构建Map对象Map<String, Object> map = new HashMap();map.put("name", "foo");map.put("value" "bar");// 构建自定义类对象MyUserInfo user = new MyUserInfo();user.setUsername("Jack");user.setAge(20);// 创建JSON格式请求体// 按自定义对象形式添加请求体数据项Forest.post("/")    .contentTypeJson()  // 指定请求体为JSON格式    .addBody("a", 1)    // 添加Body键值对: a, 1    .addBody("b", 2)    // 添加Body键值对: b, 2    .addBody(map)       // 添加Map对象到请求体中    .addBody(user);     // 添加自定义类对象到请求体中// 最终请求体数据为// {"a": 1, "b", 2, "name": "foo": "value", "bar": "username": "Jack&age": 20}

最后,介绍一种最简单直白的添加JSON请求体的办法

// 创建JSON格式请求体// 按字符串形式添加请求体数据项Forest.post("/")     // 指定请求体为JSON格式    .contentTypeJson()     // 插入JSON字符串到请求体中    .addBody("{\"name\": \"foo\", \"value\": \"bar\"}")// 最终请求体数据为// {"name": "foo", "value": "bar"}

XML数据#

在调用 addBody 方法前,需要先指定 Content-Type 为 application/xml 或其他xml字符串结尾的 Content-Type 值

Forest仅支持用JAXB配合自定义类对象的形式添加XML格式请求体

@XmlRootElement(name = "user")public MyUserInfo {
    private String usrname;
    private String password;
    public String getUsername() {        return username;    }
    public void setUsername(String username) {        this.username = username;    }
    public String getPassword() {        return password;    }
    public void setPassword(String password) {        this.password = password;    }}

在Body中插入 MyUserInfo 对象

// 构建自定义类对象MyUserInfo user = new MyUserInfo();user.setUsername("foo");user.setPassword("bar");// 创建XML格式请求体// 按自定义对象形式添加请求体数据项Forest.post("/")    .contentTypeXml()  // 指定请求体为JSON格式    .addBody(user);     // 添加自定义类对象到请求体中// 最终请求体数据为// <user><username>foo</username><password>bar</password></user>

同样,XML格式请求体也可以用字符串形式直接插入

// 创建JSON格式请求体// 按字符串形式添加请求体数据项Forest.post("/")     // 指定请求体为JSON格式    .contentTypeJson()     // 插入XML字符串到请求体中    .addBody("<user><username>foo</username><password>bar</password></user>")// 最终请求体数据为// <user><username>foo</username><password>bar</password></user>

文件#

在调用 addBody 方法前,需要先指定 Content-Type 为 multipart/form-data

然后调用 addFile 方法,添加文件数据

添加 File 对象

addFile(String name, File file, String filename, String contentType) 添加文件 (接受File对象)

  • 参数 name: 参数名称

  • 参数 file: 文件, File 类型对象

  • 参数 filename: 文件名, 期望上传的文件数据在服务端保存的文件名

  • 参数 contentType: 文件数据的 Content-Type

addFile(String name, File file, String filename) 添加文件 (接受File对象)

addFile(String name, File file) 添加文件 (接受File对象)

// 构建File对象File file = new File(path);Forest.post("/upload")      // 指定请求体为Multipart格式     .contentTypeMultipartFormData()      // 添加File对象     .addFile("file", file);

添加输入流对象

  • addFile(String name, InputStream inputStream, String filename, String contentType) 添加文件 (接受输入流)

参数 name: 参数名称

参数 inputStream: 文件输入流对象

参数 filename: 文件名, 期望上传的文件数据在服务端保存的文件名

参数 contentType: 文件数据的 Content-Type

  • addFile(String name, InputStream inputStream, String filename) 添加文件 (接受输入流)
// 构建InputStream对象File file = new File(path);InputStream in = new FileInputStream(file);Forest.post("/upload")      // 指定请求体为Multipart格式     .contentTypeMultipartFormData()      // 添加InputStream对象     .addFile("file", in, "test.jpg");

添加字节数组

addFile(String name, byte[] bytes, String filename, String contentType) 添加文件 (接受字节数组)

  • 参数 name: 参数名称
  • 参数 bytes: 文件字节数组
  • 参数 filename: 文件名, 期望上传的文件数据在服务端保存的文件名
  • 参数 contentType: 文件数据的 Content-Type

addFile(String name, byte[] bytes, String filename) 添加文件 (接受字节数组)

// 构建字节数组byte[] byteArray = IOUtils.toByteArray(url);Forest.post("/upload")      // 指定请求体为Multipart格式     .contentTypeMultipartFormData()      // 添加字节数组     .addFile("file", byteArray, "test.jpg");

添加多个文件

Forest.post("/upload")      // 指定请求体为Multipart格式     .contentTypeMultipartFormData()      // 添加File对象1     .addFile("file1", file1);      // 添加File对象2     .addFile("file2", file2);      // 添加File对象3     .addFile("file3", file3);

Multipart#

在文件上传的时候,除了要添加文件外,通常也要传递一些普通参数

只要在调用 addFile 方法后,再调用 addBody 方法即可

Forest.post("/upload")      // 指定请求体为Multipart格式     .contentTypeMultipartFormData()      // 添加File对象     .addFile("file", file)      // 添加 Multipart 参数1     .addBody("name", "foo")      // 添加 Multipart 参数2     .addBody("value", "bar");

指定Multipart数据项的 Content-Type

addBody(String name, String contentType, Object value) 添加键值对形式Body数据

  • 参数name: 字段名
  • 参数contentType: 该请求体数据项的Content-Type
  • 参数value: 字段值
Forest.post("/upload")      // 指定请求体为Multipart格式     .contentTypeMultipartFormData()      // 添加File对象     .addFile("file", file)      // 添加 Multipart 参数1,Content-Type 为  text/plian     .addBody("name", "text/plian", "foo")      // 添加 Multipart 参数2,Content-Type 为  text/plian;charset=utf-8     .addBody("value", "text/plian;charset=utf-8", "bar");

同步/异步#

Forest请求默认为同步的,可以通过async()方法设置为异步请求

setAsync(boolean async) 设置是否异步

  • 参数aysnc: true 异步, false 同步

sync() 设置为同步

async() 设置为异步

// 构建同步 Get 请求Forest.get("/");// 构建异步 Get 请求Forest.get("/").async();// 构建同步 Get 请求Forest.get("/").sync();

回调函数#

ForestRequest 提供了众多回调函数设置的方法

请求成功/失败回调函数

onSuccess(OnSuccess onSuccess) 设置成功回调函数: 请求成功时被调用

  • 参数onSuccess: OnSuccess 接口实例

onError(OnError onError) 设置失败回调函数: 请求失败时被调用

  • 参数onError: OnError 接口实例
Forest.post("/")     // onSuccess回调函数: 请求成功时被调用    .onSuccess(((data, req, res) -> {        // data 为响应成功后返回的反序列化过的数据        // req 为Forest请求对象,即 ForestRequest 类实例        // res 为Forest响应对象,即 ForestResponse 类实例    }))     // onError回调函数: 请求失败时被调用    .onError(((ex, req, res) -> {        // ex 为请求过程可能抛出的异常对象        // req 为Forest请求对象,即 ForestRequest 类实例        // res 为Forest响应对象,即 ForestResponse 类实例    }))    // 执行请求,请求成功则执行onSuccess, 失败则执行onError    .execute();

请求重试回调函数

onRetry(OnRetry onRetry) 设置重试回调函数: 请求重试时被调用

  • 参数onRetry: OnRetry 接口实例
Forest.post("/")     // 设置最大请求重试次数为 3    .maxRetryCount(3)     // onRetry回调函数: 每次请求重试前被调用    .onRetry(((req, res) -> {        // req 为Forest请求对象,即 ForestRequest 类实例        // res 为Forest响应对象,即 ForestResponse 类实例    }))    // 执行请求,请求失败会触发 onRetry, 然后重发请求    .execute();

Cookie#

请求加载/保存 Cookie

onLoadCookie(OnLoadCookie onLoadCookie) 设置加载Cookie回调函数: 加载Cookie时调用

  • 参数onLoadCookie: OnLoadCookie 接口实例

onSaveCookie(OnSaveCookie onSaveCookie) 设置保存Cookie回调函数: 需要保存Cookie时调用

  • 参数onSaveCookie: OnSaveCookie 接口实例
Forest.post("/")     // onLoadCookie回调函数: 加载Cookie时调用    .onLoadCookie(((req, cookies) -> {        // req 为Forest请求对象,即 ForestRequest 类实例        // cookies 为Cookie集合, 需要通过请求发送的Cookie都添加到该集合    }))     // onLoadCookie回调函数: 需要保存Cookie时调用    .onLoadCookie(((req, cookies) -> {        // req 为Forest请求对象,即 ForestRequest 类实例        // cookies 为Cookie集合, 需要通过请求发送的Cookie都添加到该集合    }));

成功/失败条件#

Forest 请求每次发送后都会判断是否成功 (不成功即失败)

默认的请求成功条件如下

[x] 没有异常: 包括网络连接不通、IO错误、超时等异常

[x] 响应状态码在正常范围内: >= 100 并且 < 400

一般成功的状态码为200, 所以默认成功条件满足绝大多数场景,但也免不了有些API接口不以此为标准,比如400也为正常状态码; 还有状态码为 200, 但返回JSON数据中的状态为失败也为失败。

为适应这种,Forest 提供了可自定义的成功/失败条件

successWhen(SuccessWhen successWhen) 设置成功条件: 用于判断请求是否成功/失败

  • 参数successWhen: SuccessWhen 接口实现类实例
Forest.post("/")     // 设置成功/失败条件     // 返回 true 为成功,false 为失败    .successWhen(((req, res) -> {        // req 为Forest请求对象,即 ForestRequest 类实例        // res 为Forest响应对象,即 ForestResponse 类实例        return res.noException() &&   // 请求过程没有异常                res.statusOk() &&     // 状态码在 100 ~ 399 范围内                res.statusIsNot(203); // 但不能是 203    }))    .onError((ex, req, res) -> {        System.out.println("失败!");    })    .exeute();// 若服务端返回的状态码为 203, // 即便请求过程都成功,// 也会被认为是失败,并执行 onError

使用自定义成功/失败条件实现类

setSuccessWhen(Class<? extends SuccessWhen> conditionClass) 设置成功条件,用于判断请求是否成功/失败

  • 参数conditionClass: SuccessWhen 实现类的 Class 对象

先定义成功/失败条件实现类

// 自定义成功/失败条件实现类// 需要实现 SuccessWhen 接口public class MySuccessCondition implements SuccessWhen {
    /**     * 请求成功条件     * @param req Forest请求对象     * @param res Forest响应对象     * @return true 请求成功,false 请求失败     */    @Override    public boolean successWhen(ForestRequest req, ForestResponse res) {        return res.noException() &&   // 请求过程没有异常                res.statusOk() &&     // 状态码在 100 ~ 399 范围内                res.statusIsNot(203); // 但不能是 203    }}

通过 Forest 请求对象的setSuccessWhen(Class<? extends SuccessWhen> conditionClass)方法设置该条件类

Forest.post("/")     // 设置成功/失败条件实现类    .successWhen(MySuccessCondition.class)    .onError((ex, req, res) -> {        System.out.println("失败!");    })    .exeute();// 若服务端返回的状态码为 203, // 即便请求过程都成功,// 也会被认为是失败,并执行 onError

请求重试#

ForestRequest 对象提供了设置重试相关属性的方法

是否运行重试 (默认为开启重试)

setRetryEnabled(boolean retryEnabled) 设置是否开启请求重试

  • 参数retryEnabled: true 开启重试,false 关闭重试
// 开启请求重试(默认即开启)request.setRetryEnabled(true);// 关闭请求重试(关闭后该请求不会再触发重试)request.setRetryEnabled(false);

设置最大重试次数 (默认为0次,即不会重试)

maxRetryCount(int retryCount) 设置请求失败后的最大重试次数

  • 参数retryCount: 重试次数
// 设置请求最大重试次数为 3 次request.maxRetryCount(3);

设置最大请重试的时间间隔 (时间单位为毫秒, 默认为0毫秒)

maxRetryInterval(long maxRetryInterval) 设置最大请重试的时间间隔

  • 参数maxRetryInterval: 最大请重试的时间间隔 (毫秒)
// 设置请求最大重试次数为 10msrequest.maxRetryInterval(10L);

重试器#

Retryer 重试器,即重试策略,可以设定每次重试请求之间的时间间隔

Forest 默认重试器类为 com.dtflys.forest.retryer.BackOffRetryer,它是依据二进制退避算法的重试策略类

若配置该重试器,重试过程如下:

  • 第一次重试与第一次请求之间间隔 0的2次方 * 1s, 即0s
  • 第二次重试与第一次重试之间间隔 1的2次方 * 1s, 即1s
  • 第三次次重试与第二次重试之间间隔 2的2次方 * 1s, 即4s
  • 后面几次重试时间间隔以此类推,直到达到最大请求次数后停止重试
  • 每次时间间隔不能大于 maxRetryInterval, 若 maxRetryInterval 设置为 10, 则每次间隔只能为 10ms

您也可以自定义重试器

// 自定义重试器// 继承 BackOffRetryer 类public class MyRetryer extends BackOffRetryer {
    public MyRetryer(ForestRequest request) {        super(request);    }
    /**     * 重写 nextInterval 方法     * 该方法用于指定每次重试的时间间隔     * @param currentCount 当前重试次数     * @return 时间间隔 (时间单位为毫秒)     */    @Override    protected long nextInterval(int currentCount) {        // 每次重试时间间隔恒定为 1s (1000ms)        return 1000;    }}

再通过 ForestRequest 对象的 retryer(Class<? extends ForestRetryer> retryerClass) 设置该自定义重试器类

// 设置自定义重试器类request.retryer(MyRetryer.class);

重试条件#

Forest 请求的重试条件有两种设置模式:

一般情况下不会设置 RetryWhen 重试条件,即直通过请求的成功/失败来判断是否重试,逻辑很简单:请求成功不重试,失败就重试

但有些特殊情况,需要在请求成功的情况下也重试,满足一定业务条件后才停止重试,这种情况就需要 RetryWhen 重试条件上场了

retryWhen(RetryWhen retryWhen) 设置重试条件:用于判断请求是否触发重试

  • 参数retryWhen: RetryWhen 接口实例
Forest.get("/")     // 最大重试次数为 3    .maxRetryCount(3)     // 最大重试间隔为 10ms    .maxRetryInterval(10)     // 重试条件: 状态码为 203 就重试    .retryWhen(((req, res) -> res.statusIs(203)))     // onSuccess回调函数: 成功时调用    .onSuccess((data, req, res) -> {        System.out.println("成功!")    })     // 执行请求    .execute();// 若发送请求后,服务端返回 203 状态码// 就不断触发重试// 直到服务端不返回 203,或达到最大重试次数,停止重试// 若最后一次重试服务端发送的还是 203,则认为请求成功,执行 onSuccess

先定义自定义请求重试实现类

// 自定义重试条件类// 需要实现 RetryWhen 接口public class MyRetryCondition implements RetryWhen {    /**     * 请求重试条件     * @param req Forest请求对象     * @param res Forest响应对象     * @return true 重试,false 不重试     */    @Override    public boolean retryWhen(ForestRequest req, ForestResponse res) {        // 响应状态码为 203 就重试        return res.statusIs(203);    }}

再通过调用 retryWhen(Class<? extends RetryWhen> conditionClass) 方法设置自定义重试条件类

Forest.get("/")     // 最大重试次数为 3    .maxRetryCount(3)     // 最大重试间隔为 10ms    .maxRetryInterval(10)     // 重试条件: 状态码为 203 就重试    .retryWhen(MyRetryCondition.class)     // onSuccess回调函数: 成功时调用    .onSuccess((data, req, res) -> {        System.out.println("成功!");    })     // 执行请求    .execute();// 若发送请求后,服务端返回 203 状态码// 就不断触发重试// 直到服务端不返回 203,或达到最大重试次数,停止重试// 若最后一次重试服务端发送的还是 203,则认为请求成功,执行 onSuccess

重定向#

Forest 在默认情况下会自动重定向,即服务端返回 301302307 等状态码时,会自动跳转到新的转移地址发送请求

回调函数 onRedirection 可以帮助您在重定向地址转移请求发送前触发,以此获得以一次的Request和Response对象

onRedirection(OnRedirection onRedirection) 设置重定向回调函数: 在请求重定向时触发

  • 参数onRedirection: OnRedirection 接口实例
Forest.get("/")    .onRedirection((redirectReq, prevReq, prevRes) -> {        // redirectReq: 重定向转移地址请求对象        // prevReq: 重定向转移前的请求对象        // prevRes: 重定向转移前获得的响应对象    })     // 执行请求    .execute();

自动重定向开关#

当然您也可以打开/关闭 Forest 请求的自动重定向

autoRedirects(boolean autoRedirects) 设置是否打开自动重定向

  • 参数autoRedirects: true 打开自动重定向, false 禁止自动重定向
ForestResponse response = Forest.get("/")         // 关闭自动重定向         .autoRedirects(false)         // 执行请求           .execute(ForestResponse.class);// 最终这里的 response 将获取第一次请求的结果// 而不是自动重定向转移后的响应结果

代理#

ForestRequest 提供了设置和获取正向代理的方法

proxy(ForestProxy proxy) 设置正向代理

  • 参数proxy 正向代理,ForestProxy 类实例
// 构建Forest正向代理对象// 设置代理服务器的主机地址和端口号ForestProxy proxy = new ForestProxy("127.0.0.1", 3128);// 设置代理服务验证的用户名和密码proxy.setUsername("foo");proxy.setPassword("bar");// 为请求设置正向代理request.proxy(proxy);