Skip to main content
Version: 1.5.x

请求地址

HTTP请求可以没有请求头、请求体,但一定会有请求地址,即URL,以及很多请求的参数都是直接绑定在URLQuery部分上

设置URL#

基本URL设置方法就如 《简单请求》 的例子所示,只要在url属性中填入完整的请求地址即可。

除此之外,也可以通过 @Var 注解修饰的参数从外部动态传入URL:

/** * 整个完整的URL都通过参数传入 * {0}代表引用第一个参数 */@Get("{0}")String send1(String myURL);
/** * 整个完整的URL都通过 @Var 注解修饰的参数动态传入 */@Get("{myURL}")String send2(@Var("myURL") String myURL);
/** * 通过参数转入的值作为URL的一部分 */@Get("http://{myURL}/abc")String send3(@Var("myURL") String myURL);
/** * 参数转入的值可以作为URL的任意一部分 */@Get("http://localhost:8080/test/{myURL}?a=1&b=2")String send4(@Var("myURL") String myURL);

URL结构#

再继续介绍之后的Forest URL相关内容之前,我认为有必要先讲解下URL的标准结构 (已经对此非常了解的小伙伴可以跳过此节)

一个标准的URL一般会包含以下几个部分:

  • 协议: 如URL的协议部分为http ,就代表请求使用的是HTTP协议
  • 域名: 如URL的域名部分为baidu.com,就代表请求域名地址为baidu.com,IP地址和主机名也可以当作域名一样使用
  • 端口号: 跟在域名后面的是端口,域名和端口之间使用:作为分隔符。端口不是一个URL必须的部分,如果省略端口部分,HTTP协议请求将采用默认端口80,HTTPS则用默认端口443
  • 路径地址: 路径地址是从域名后的第一个/开始,一直到?为止的部分,如果没有?,则是从域名后的最后一个/开始到#为止的部分,如果没有#,那么从域名后的第一个/开始到结束,都是路径地址部分
  • 查询参数: 从开始到#为止之间的部分为查询参数部分,又称搜索部分、查询部分
  • : 从#开始到最后,都是锚部分
url

根地址#

我们都知道,一个URL地址包含协议、站点地址(域名/IP地址/主机名)、端口号、路径地址、查询参数等几个部分。

而协议、站点地址(域名/IP地址/主机名)、端口号这三部分称为根地址。

但如果代码中大量URL接口都来自同一个站点,那就会存在两大重复的域名或IP地址,如果它们都以字符串形式散落在各个Forest请求接口方法的URL属性中,那就会变得难以维护。

@Address 注解#

Forest 从1.5.3版本开始提供了 @Address 注解,帮助您将URL的地址部分提取出来,方便管理

// 通过 @Address 注解绑定根地址// host 绑定到第一个参数, port 绑定到第二个参数@Post("/data")@Address(host = "{0}", port = "{1}")ForestRequest<String> sendHostPort(String host, int port);
// 若调用 sendHostPort("192.168.0.2", 8080);// 则最终产生URL:// http://192.168.0.2:8080/data

@Address 注解可以绑定到接口类上,根地址绑定返回就扩展到整个接口下的所有方法

// 整个接口下的所有方法请求都默认绑定该根地址@Address(host = "127.0.0.1", port = "8080")public interface MyClient {
    // 绑定接口上的默认根地址    // 最终URL: http://127.0.0.1:8080/data1    @Post("/data1")    ForestRequest<String> sendData1();
    // 绑定接口上的默认根地址    // 最终URL: http://127.0.0.1:8080/data2    @Post("/data2")    ForestRequest<String> sendData2();
    // 使用方法上的根地址    // 最终URL: http://192.168.0.1:7000/data3    @Post("/data3")    @Address(host = "192.168.0.1", port = "7000")    ForestRequest<String> sendData3();}

动态根地址#

以上的方法虽然可以方便管理和批量配置多个请求的根地址,但如果想要实时动态地变更根地址就很困难

比如:我想每次发送请求的时候,动态地从3个IP地址中随机选取一个作为根地址,该怎么做呢?

Forest 提供了地址来源接口,即AddressSource接口来帮您实现该功能

// 实现 AddressSource 接口public class MyAddressSource implements AddressSource {
    @Override    public ForestAddress getAddress(ForestRequest request) {        // 定义 3 个 IP 地址        String[] ipArray = new String[] {                "192.168.0.1",                "192.168.0.2",                "192.168.0.3",        };        // 随机选出其中一个        Random random = new Random();        int i = random.nextInt(3);        String ip = ipArray[i];        // 返回 Forest 地址对象        return new ForestAddress(ip, 80);    }}

绑定自定义的AddressSource接口实现类

// 也是通过 @Address 注解来绑定动态地址来源// 每次调用该方法,都可能是不同的根地址@Post("/data")@Address(source = MyAddressSource.class)ForestRequest<String> sendData();

若连续调用多次sendData(),则每次请求的URL根地址都可能会不同

myClient.sendData(); // 第一次调用, URL: http://192.168.0.2/datamyClient.sendData(); // 第二次调用, URL: http://192.168.0.2/datamyClient.sendData(); // 第三次调用, URL: http://192.168.0.1/datamyClient.sendData(); // 第四次调用, URL: http://192.168.0.3/datamyClient.sendData(); // 第五次调用, URL: http://192.168.0.1/data

URL参数#

字符串模板传参#

HTTP的URL不光有协议名、域名、端口号等等基本信息,更为重要的是它能携带各种参数,称为Query参数,它通常包含参数名和参数值两部分。

Forest给URLQuery部分传参也有多种方式,其中最简洁直白的就数字符串拼接了。

/** * 直接在url字符串的问号后面部分直接写上 参数名=参数值 的形式 * 等号后面的参数值部分可以用 {参数序号} 这种字符串模板的形式替代 * 在发送请求时会动态拼接成一个完整的URL * 使用这种形式不需要为参数定义额外的注解 *  * 注:参数序号是从 0 开始记的方法参数的序号 * 0 代表第一个参数,1 代表第二个参数,以此类推 */@Request("http://localhost:8080/abc?a={0}&b={1}&id=0")String send1(String a, String b);
/** * 直接在url字符串的问号后面部分直接写上 参数名=参数值 的形式 * 等号后面的参数值部分可以用 {变量名} 这种字符串模板的形式替代 * 在发送请求时会动态拼接成一个完整的URL * 使用这种方式需要通过 @Var 注解或全局配置声明变量 */@Request("http://localhost:8080/abc?a={a}&b={b}&id=0")String send2(@Var("a") String a, @Var("b") String b);

/** * 如果一个一个变量包含多个Query参数,比如: "a=1&b=2&c=3" * 为变量 parameters 的字符串值 * 就用 ${变量名} 这种字符串模板格式 * 使用这种方式需要通过 @Var 注解或全局配置声明变量 */@Request("http://localhost:8080/abc?${parameters}")String send3(@Var("parameters") String parameters);

调用{参数序号}字符串模板的方法

// 会对第二个参数 B&c=C 进行URL EncodemyClient.send1("A", "B&c=C");
// 产生的URL为// http://localhost:8080/abc?a=A&b=B%26c%3DC&id=0

调用{变量名}字符串模板的方法

// 会对第二个参数 B&c=C 进行URL EncodemyClient.send2("A", "B&c=C");
// 产生的URL为// http://localhost:8080/abc?a=A&b=B%26c%3DC&id=0

调用${变量名}字符串模板的方法

// 会用参数输入的字符串替换URL中的 ${parameters} 部分myClient.send3("a=A&b=B&c=C");
// 产生的URL为// http://localhost:8080/abc?a=A&b=B&c=C
文档导航

关于字符串模板的详细用法,请参见《模板表达式

@Query 注解#

但把所有Query参数直接写在url属性的字符串里面是不是也太简单粗暴了,有没有优雅点的方式?有的。


/** * 使用 @Query 注解,可以直接将该注解修饰的参数动态绑定到请求url中 * 注解的 value 值即代表它在url的Query部分的参数名 */@Request("http://localhost:8080/abc?id=0")String send(@Query("a") String a, @Query("b") String b);
友情提示

@Query 注解修饰的参数一定会出现在 URL 中。

若是要传的URL参数太多了呢?难道要我在方法上定义十几二十个@Query修饰的参数?那也太难看了吧。别急,Forest还是有办法让您变的代码变得优雅的。


/** * 使用 @Query 注解,可以修饰 Map 类型的参数 * 很自然的,Map 的 Key 将作为 URL 的参数名, Value 将作为 URL 的参数值 * 这时候 @Query 注解不定义名称 */@Get("http://localhost:8080/abc?id=0")String send1(@Query Map<String, Object> map);

/** * @Query 注解也可以修饰自定义类型的对象参数 * 依据对象类的 Getter 和 Setter 的规则取出属性 * 其属性名为 URL 参数名,属性值为 URL 参数值 * 这时候 @Query 注解不定义名称 */@Get("http://localhost:8080/abc?id=0")String send2(@Query UserInfo user);

是不是瞬间简洁不少,但用@Query注解绑定参数的时候也有需要注意的地方:

caution
  • (1) 需要单个单个定义 参数名=参数值 的时候,@Query注解的value值一定要有,比如 @Query("name") String name

  • (2) 需要绑定对象的时候,@Query注解的value值一定要空着,比如 @Query User user 或 @Query Map map

数组参数#

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

/* * 接受列表参数为URL查询参数 */@Get("http://localhost:8080/abc")String send1(@Query("id") List idList);
// 若调用 send1(Arrays.asList(1, 2, 3, 4))// 则产生的最终URL为// http://localhost:8080/abc?id=1&id=2&id=3&id=4
/* * 接受数组参数为URL查询参数 */@Get("http://localhost:8080/abc")String send2(@Query("id") int[] idList);
// 若调用 send2(new int[] {1, 2, 3, 4})// 则产生的最终URL为// http://localhost:8080/abc?id=1&id=2&id=3&id=4

若要产生带方括号[]参数名的数组URL参数样式,可以使用 @ArrayQuery 注解

/* * 接受列表参数为URL查询参数 */@Get("http://localhost:8080/abc")String send1(@ArrayQuery("id") List idList);
// 若调用 send1(Arrays.asList(1, 2, 3, 4))// 则产生的最终URL为// http://localhost:8080/abc?id[]=1&id[]=2&id[]=3&id[]=4
/* * 接受数组参数为URL查询参数 */@Get("http://localhost:8080/abc")String send2(@ArrayQuery("id") int[] idList);
// 若调用 send2(new int[] {1, 2, 3, 4})// 则产生的最终URL为// http://localhost:8080/abc?id[]=1&id[]=2&id[]=3&id[]=4

JSON参数#

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

这时,可以使用@JSONQuery注解

@Get("http://localhost:8080/abc")String send(@JSONQuery("id") List idList);
// 若调用 send(Arrays.asList(1, 2, 3, 4))// 则产生的最终URL为// http://localhost:8080/abc?id=[1, 2, 3, 4]// 注意:这里的JSON数据最终会被 URLEncode// 所以最终请求的参数为 http://localhost:8080/abc?id=%5B1%2C%202%2C%203%2C%204%5D