使用参数的Spring WebClient请求
最后修改:2019年9月2日
1.概述
很多框架和项目正在介绍响应式编程和异步请求处理。因此,春天5介绍了活性WebClient.实施作为其中的一部分webflux框架。
在本教程中,我们将看到如何反应地消耗休息API端点WebClient.。
2. REST API端点
首先,让我们定义一个样本休息API.与下面是获取端点的方法:
- /产品- 获取所有产品
- /产品/ {id}-通过ID获取产品
- /产品/ {id} /属性/ {attributeId}—通过id获取产品属性
- /产品/?名称= {name}&traveringdate = {transportdate}&color = {color}找到产品
- / /产品?标签[]={标签1}标记[]={标签2}- 通过标记获取产品
- / /产品?类别= {category1}类别= {category2}-按类别获取产品
所以,我们刚刚定义了一些不同的URI。在短短的一刻,我们将弄清楚如何构建和发送每种类型的URIWebClient.。
请注意,按标签和按类别获取产品的uri包含数组作为查询参数。然而,语法是不同的。自对于在uri中如何表示数组没有严格的定义。主要是,这取决于服务器端实现。因此,我们将介绍这两种情况。
3.WebClient.设置
首先,我们需要创建一个实例WebClient.。对于本文,我们将使用嘲笑的对象据我们所需要只是为了验证是否要求有效的URI。
让我们定义客户端和相关模拟对象:
this.exchangeFunction = Mock(ExpectFunction.class);clientresponse mockresponse = mock(clientresponse.class);什么时候(这个.exchangefunction.exchange(this.argumentcaptor.capture()))。然后return(mono.just(mockresponse));这个.webclient = webclient .builder().baseull(“https://example.com/api”).exchangefunction(ExpectionFunction).build();
此外,我们还传递了一个基URL,该URL将被添加到客户机发出的所有请求中。
最后,验证特定的URI已经传递到底层Expection.实例,让我们使用下面的helper方法:
私有void验证cledull(String ContctoryURL){ClientRequest请求= this.argumentCaptor.getValue();assert.assertequals(string.format(“%s%s”,base_url,contgureurl),请求。url()。toString());mockito.verify(this.exchangefunction).exchange(请求);verifynomore interactions(this.exchangefunction);}
的WebClientBuilder.类的Uri()方法提供uribuilder.实例作为参数。通常,通常以下列方式进行API调用:
this.webclient.get()。uri(uribuilder - > ulibuilder // ...构建URI .Build()).Retrieve();
我们会使用uribuilder.本指南将详细介绍如何构造uri。值得注意的是,我们可以使用任何其他方式构建URI,然后将生成的URI作为字符串传递。
4.URI路径组件
路径组件由一系列用斜杠(/)分隔的路径段组成。。首先,当URI没有任何可变段时,让我们从一个简单的情况开始/产品:
this.webClient.get () .uri(“/产品”).retrieve ();verifyCalledUrl(“/产品”);
在这种情况下,我们可以传递a字符串作为一个论点。
接下来,让我们带/产品/ {id}端点,并构建相应的URI:
this.webclient.get()。uri(uribuilder - > ulibuilder .path(“/ products / {id}”).build(2)).retrieve();验证程序(“/ products / 2”);
从上面的代码中,我们可以看到实际的段值传递给build ()方法。
现在,我们可以用类似的方式为/产品/ {id} /属性/ {attributeId}端点:
urbuilder .path("/products/{id}/attributes/{attributeId}") .build(2, 13)) .retrieve();verifyCalledUrl(“/产品/ 2 /属性/ 13”);
URI可以根据需要具有多种路径段。当然,如果最终的URI长度不超过限制。最后,请记住保持转移到的实际段值的正确顺序build ()方法。
5. URI查询参数
通常,查询参数是一个简单的键值对title 金宝搏188体育= Baeldung。让我们看看如何构建这样的uri。
5.1。单值参数
让我们从单值参数开始并拍摄/产品/?名称= {name}&traveringdate = {transportdate}&color = {color}端点。要设置查询参数,我们调用queryParam ()方法的方法uribuilder.接口:
这个。webclient .get() .uri(urilbuilder - > urilbuilder .path("/products/") .queryParam("name", "AndroidPhone") .queryParam("color", "black") .queryParam("deliveryDate", "13/04/2019") .build()) .retrieve();verifyCalledUrl(“/产品/ ?名字= AndroidPhone&color = black&deliveryDate = 13/04/2019”);
在这里,我们已添加三个查询参数并立即分配实际值。此外,还可以留下占位符而不是确切的值:
this.webclient.get()。uri(uribuilder - > ulibuilder .path(“/ products /”).queryparam(“名称”,“{title}”).queryparam(“color”,“{authorid}”)。queryparam(“transportdate”,“{date}”).build(“androidphone”,“黑色”,“13/04/2019”)).Retrieve();VerifyCalledURL(“/产品/?名称= AndroidPhone&Color = Black&DeliveryDate = 13%2F04%2F2019”);
特别是,当在链中进一步传递生成器对象时,这可能会有帮助。请注意一个重要的上面的两个代码片段之间的差异。
注意所期望的uri,我们可以看到它们是以不同的方式编码。特别是斜杠字符(/)在上一个示例中被转义。一般来说,RFC3986在查询中不需要对斜杠进行编码。
然而,一些服务器端应用程序可能需要这样的转换。因此,我们将在本指南的后面部分看到如何改变这种行为。
5.2。数组参数
同样,我们可能需要传递一个值数组。不过,在查询字符串中传递数组没有严格的规则。因此,查询字符串中的数组表示形式因项目而异,通常取决于底层框架。我们将涵盖最广泛使用的格式。
让我们从/ /产品?标签[]={标签1}标记[]={标签2}端点:
this.webclient.get().uri(uribuilder - > ulibuilder .path(“/ products /”).queryparam(“tag []”,“snapdragon”,“nfc”).build()).retrieve();VerifyCalledURL(“/产品/?标签%5B%5d = Snapdragon&Tag%5b%5d = nfc”);
正如我们所看到的,最终的URI包含多个标记参数,后跟经过编码的方括号。的queryParam ()方法接受变量参数作为值,因此不需要多次调用该方法。
或者,我们可以省略方括号并刚用相同的键传递多个查询参数,但不同的价值观 -/ /产品?类别= {category1}类别= {category2}:
this.webclient.get().uri(uribuilder - > ulibuilder .path(“/ products /”).queryparam(“类别”,“手机”,“平板电脑”).build()).Retrieve();VerifyCalledURL(“/产品/?类别=手机和类别=平板电脑);
最后,还有一种更常用的方法来编码数组,那就是传递以逗号分隔的值。让我们把前面的例子转换成逗号分隔的值:
urilbuilder .path("/products/") . queryparam ("category",字符串。加入(“,”、“手机”、“平板电脑”)).build ()) .retrieve ();verifyCalledUrl(“类别/产品/ ? =手机、平板电脑”);
因此,我们只是使用加入()方法的方法字符串类创建以逗号分隔的字符串。当然,我们可以使用应用程序所期望的任何其他分隔符。
6.编码模式
还记得我们前面提到的URL编码吗?
如果默认行为不适合我们的要求,我们可以更改它。我们需要提供一个UriBuilderFactory在构建WebClient.实例。在本例中,我们将使用defaulturibuilderfactory.班级。设置编码呼叫setEncodingMode ()方法。以下模式可用:
- template_and_values.:对URI模板进行预编码,并在展开时对URI变量进行严格编码
- VALUES_ONLY:不要对URI模板进行编码,而是在将URI变量展开到模板后对其进行严格编码
- URI_COMPONENTS:过期URI变量后,对URI组件值进行编码
- 没有任何:未应用编码
默认值是template_and_values.。让我们将模式设置为URI_COMPONENTS:
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);factory.setEncodingMode (DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT);这一点。webClient = webClient .builder() .uriBuilderFactory(factory) .baseUrl(BASE_URL) .exchangeFunction(exchangeFunction) .build();
因此,下面的断言将成功:
这个。webclient .get() .uri(urilbuilder - > urilbuilder .path("/products/") .queryParam("name", "AndroidPhone") .queryParam("color", "black") .queryParam("deliveryDate", "13/04/2019") .build()) .retrieve();verifyCalledUrl(“/产品/ ?名字= AndroidPhone&color = black&deliveryDate = 13/04/2019”);
当然,我们也可以提供完全定制的UriBuilderFactory实现手动处理URI创建。
7.结论
在本教程中,我们已经看到了如何使用不同类型的URI使用WebClient.和DefaultUribuilder。
沿途,我们涵盖了各种类型和格式的查询参数。我们用更改URL Builder的默认编码模式来包裹。
与往常一样,本文中的所有代码片段都是可用的在GitHub存储库。