Skip to content

Commit 4bce596

Browse files
authored
Add support for form parameters (application/x-www-form-urlencoded) to the FlowableHttpClient (#4055)
1 parent 2b8522a commit 4bce596

File tree

6 files changed

+143
-0
lines changed

6 files changed

+143
-0
lines changed

modules/flowable-http-common/src/main/java/org/flowable/http/common/api/HttpRequest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
import java.util.ArrayList;
1616
import java.util.Collection;
17+
import java.util.LinkedHashMap;
18+
import java.util.List;
19+
import java.util.Map;
1720

1821
import org.flowable.common.engine.api.FlowableIllegalStateException;
1922

@@ -31,7 +34,16 @@ public class HttpRequest {
3134
protected HttpHeaders secureHttpHeaders;
3235
protected String body;
3336
protected String bodyEncoding;
37+
/**
38+
* Multi-value parts to be sent in the request.
39+
* This is used for multipart/form-data requests and will be encoded as such.
40+
*/
3441
protected Collection<MultiValuePart> multiValueParts;
42+
/**
43+
* Form parameters to be sent in the request.
44+
* This is used for form submissions and will be encoded as application/x-www-form-urlencoded.
45+
*/
46+
protected Map<String, List<String>> formParameters;
3547
protected int timeout;
3648
protected boolean noRedirects;
3749

@@ -83,6 +95,8 @@ public String getBody() {
8395
public void setBody(String body) {
8496
if (multiValueParts != null && !multiValueParts.isEmpty()) {
8597
throw new FlowableIllegalStateException("Cannot set both body and multi value parts");
98+
} else if (formParameters != null && !formParameters.isEmpty()) {
99+
throw new FlowableIllegalStateException("Cannot set both body and form parameters");
86100
}
87101
this.body = body;
88102
}
@@ -102,13 +116,31 @@ public Collection<MultiValuePart> getMultiValueParts() {
102116
public void addMultiValuePart(MultiValuePart part) {
103117
if (body != null) {
104118
throw new FlowableIllegalStateException("Cannot set both body and multi value parts");
119+
} else if (formParameters != null && !formParameters.isEmpty()) {
120+
throw new FlowableIllegalStateException("Cannot set both form parameters and multi value parts");
105121
}
106122
if (multiValueParts == null) {
107123
multiValueParts = new ArrayList<>();
108124
}
109125
multiValueParts.add(part);
110126
}
111127

128+
public Map<String, List<String>> getFormParameters() {
129+
return formParameters;
130+
}
131+
132+
public void addFormParameter(String key, String value) {
133+
if (body != null) {
134+
throw new FlowableIllegalStateException("Cannot set both body and form parameters");
135+
} else if (multiValueParts != null && !multiValueParts.isEmpty()) {
136+
throw new FlowableIllegalStateException("Cannot set both multi value parts and form parameters");
137+
}
138+
if (formParameters == null) {
139+
formParameters = new LinkedHashMap<>();
140+
}
141+
formParameters.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
142+
}
143+
112144
public int getTimeout() {
113145
return timeout;
114146
}

modules/flowable-http-common/src/main/java/org/flowable/http/common/impl/apache/ApacheHttpComponentsFlowableHttpClient.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.net.URI;
1818
import java.net.URISyntaxException;
1919
import java.nio.charset.Charset;
20+
import java.util.ArrayList;
2021
import java.util.List;
2122
import java.util.Map;
2223
import java.util.function.Consumer;
@@ -33,6 +34,7 @@
3334
import org.apache.http.client.HttpClient;
3435
import org.apache.http.client.ResponseHandler;
3536
import org.apache.http.client.config.RequestConfig;
37+
import org.apache.http.client.entity.UrlEncodedFormEntity;
3638
import org.apache.http.client.methods.CloseableHttpResponse;
3739
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
3840
import org.apache.http.client.methods.HttpGet;
@@ -53,6 +55,7 @@
5355
import org.apache.http.impl.client.CloseableHttpClient;
5456
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
5557
import org.apache.http.impl.client.HttpClientBuilder;
58+
import org.apache.http.message.BasicNameValuePair;
5659
import org.apache.http.params.HttpParams;
5760
import org.apache.http.protocol.HTTP;
5861
import org.apache.http.protocol.HttpContext;
@@ -255,6 +258,21 @@ protected void setRequestEntity(HttpRequest requestInfo, HttpEntityEnclosingRequ
255258
+ " If you want to use, please make sure that the org.apache.httpcomponents:httpmime dependency is available");
256259
}
257260

261+
} else if (requestInfo.getFormParameters() != null) {
262+
Map<String, List<String>> formParameters = requestInfo.getFormParameters();
263+
List<BasicNameValuePair> parameters = new ArrayList<>(formParameters.size());
264+
for (Map.Entry<String, List<String>> entry : formParameters.entrySet()) {
265+
String name = entry.getKey();
266+
for (String value : entry.getValue()) {
267+
parameters.add(new BasicNameValuePair(name, value));
268+
}
269+
}
270+
271+
if (StringUtils.isNotEmpty(requestInfo.getBodyEncoding())) {
272+
requestBase.setEntity(new UrlEncodedFormEntity(parameters, requestInfo.getBodyEncoding()));
273+
} else {
274+
requestBase.setEntity(new UrlEncodedFormEntity(parameters));
275+
}
258276
}
259277
}
260278

modules/flowable-http-common/src/main/java/org/flowable/http/common/impl/apache/client5/ApacheHttpComponents5FlowableHttpClient.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.net.URI;
1919
import java.net.URISyntaxException;
2020
import java.nio.charset.Charset;
21+
import java.util.ArrayList;
2122
import java.util.Iterator;
2223
import java.util.List;
2324
import java.util.Map;
@@ -50,11 +51,13 @@
5051
import org.apache.hc.core5.http.ParseException;
5152
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
5253
import org.apache.hc.core5.http.io.entity.EntityUtils;
54+
import org.apache.hc.core5.http.message.BasicNameValuePair;
5355
import org.apache.hc.core5.http.nio.AsyncRequestProducer;
5456
import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers;
5557
import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder;
5658
import org.apache.hc.core5.io.CloseMode;
5759
import org.apache.hc.core5.io.ModalCloseable;
60+
import org.apache.hc.core5.net.WWWFormCodec;
5861
import org.apache.hc.core5.ssl.SSLContextBuilder;
5962
import org.apache.hc.core5.util.TimeValue;
6063
import org.flowable.common.engine.api.FlowableException;
@@ -241,6 +244,20 @@ protected void setRequestEntity(HttpRequest requestInfo, AsyncRequestBuilder req
241244
} catch (IOException e) {
242245
throw new FlowableException("Cannot create multi part entity", e);
243246
}
247+
} else if (requestInfo.getFormParameters() != null) {
248+
Map<String, List<String>> formParameters = requestInfo.getFormParameters();
249+
List<BasicNameValuePair> parameters = new ArrayList<>(formParameters.size());
250+
for (Map.Entry<String, List<String>> entry : formParameters.entrySet()) {
251+
String name = entry.getKey();
252+
for (String value : entry.getValue()) {
253+
parameters.add(new BasicNameValuePair(name, value));
254+
}
255+
}
256+
257+
Charset charset = requestInfo.getBodyEncoding() != null ? Charset.forName(requestInfo.getBodyEncoding()) : null;
258+
259+
String formParametersString = WWWFormCodec.format(parameters, charset);
260+
requestBase.setEntity(AsyncEntityProducers.create(formParametersString, ContentType.APPLICATION_FORM_URLENCODED.withCharset(charset)));
244261
}
245262
}
246263

modules/flowable-http-common/src/main/java/org/flowable/http/common/impl/spring/reactive/SpringWebClientFlowableHttpClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.http.ResponseEntity;
4242
import org.springframework.http.client.MultipartBodyBuilder;
4343
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
44+
import org.springframework.util.CollectionUtils;
4445
import org.springframework.web.reactive.function.BodyInserters;
4546
import org.springframework.web.reactive.function.client.WebClient;
4647

@@ -211,6 +212,8 @@ protected void setRequestEntity(HttpRequest requestInfo, WebClient.RequestBodySp
211212
}
212213

213214
requestBodySpec.body(BodyInserters.fromMultipartData(multipartBodyBuilder.build()));
215+
} else if (requestInfo.getFormParameters() != null) {
216+
requestBodySpec.body(BodyInserters.fromFormData(CollectionUtils.toMultiValueMap(requestInfo.getFormParameters())));
214217
}
215218
}
216219

modules/flowable-http-common/src/test/java/org/flowable/http/common/api/HttpRequestTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ void setBodyShouldNotBePossibleWhenMultiValuePartsAreSet() {
3333
assertThat(request.getBody()).isNull();
3434
}
3535

36+
@Test
37+
void setBodyShouldNotBePossibleWhenFormParametersAreSet() {
38+
HttpRequest request = new HttpRequest();
39+
request.addFormParameter("name", "kermit");
40+
assertThatThrownBy(() -> request.setBody("test"))
41+
.isInstanceOf(FlowableIllegalStateException.class)
42+
.hasMessage("Cannot set both body and form parameters");
43+
assertThat(request.getBody()).isNull();
44+
}
45+
3646
@Test
3747
void addMultiValuePartShouldNotBePossibleWhenBodyIsSet() {
3848
HttpRequest request = new HttpRequest();
@@ -43,6 +53,36 @@ void addMultiValuePartShouldNotBePossibleWhenBodyIsSet() {
4353
assertThat(request.getMultiValueParts()).isNull();
4454
}
4555

56+
@Test
57+
void addMultiValuePartShouldNotBePossibleWhenFormParametersAreSet() {
58+
HttpRequest request = new HttpRequest();
59+
request.addFormParameter("name", "kermit");
60+
assertThatThrownBy(() -> request.addMultiValuePart(MultiValuePart.fromText("name", "kermit")))
61+
.isInstanceOf(FlowableIllegalStateException.class)
62+
.hasMessage("Cannot set both form parameters and multi value parts");
63+
assertThat(request.getMultiValueParts()).isNull();
64+
}
65+
66+
@Test
67+
void addFormParameterShouldNotBePossibleWhenBodyIsSet() {
68+
HttpRequest request = new HttpRequest();
69+
request.setBody("test");
70+
assertThatThrownBy(() -> request.addFormParameter("name", "kermit"))
71+
.isInstanceOf(FlowableIllegalStateException.class)
72+
.hasMessage("Cannot set both body and form parameters");
73+
assertThat(request.getFormParameters()).isNull();
74+
}
75+
76+
@Test
77+
void addFormParameterShouldNotBePossibleWhenMultiValuePartsAreSet() {
78+
HttpRequest request = new HttpRequest();
79+
request.addMultiValuePart(MultiValuePart.fromText("name", "kermit"));
80+
assertThatThrownBy(() -> request.addFormParameter("name", "fozzie"))
81+
.isInstanceOf(FlowableIllegalStateException.class)
82+
.hasMessage("Cannot set both multi value parts and form parameters");
83+
assertThat(request.getFormParameters()).isNull();
84+
}
85+
4686
@Test
4787
void getHttpHeadersAsString() {
4888
HttpRequest request = new HttpRequest();

modules/flowable-http/src/test/java/org/flowable/http/FlowableHttpClientTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,39 @@ void postWithMultiPartWithMimeType(FlowableHttpClient httpClient) {
224224
.containsExactly("application/json");
225225
}
226226

227+
@ParameterizedTest
228+
@ArgumentsSource(FlowableHttpClientArgumentProvider.class)
229+
void postWithFormParameters(FlowableHttpClient httpClient) {
230+
HttpRequest request = new HttpRequest();
231+
request.setUrl("http://localhost:9798/api/test-form?queryArg=testFormParameters");
232+
request.setMethod("POST");
233+
request.addFormParameter("name", "kermit");
234+
request.addFormParameter("name", "fozzie");
235+
HttpHeaders httpHeaders = new HttpHeaders();
236+
httpHeaders.add("X-Test", "Test Form Parameters Value");
237+
request.setHttpHeaders(httpHeaders);
238+
HttpResponse response = httpClient.prepareRequest(request).call();
239+
240+
assertThatJson(response.getBody())
241+
.when(Option.IGNORING_EXTRA_FIELDS)
242+
.isEqualTo("""
243+
{
244+
url: 'http://localhost:9798/api/test-form',
245+
args: {
246+
queryArg: [ 'testFormParameters' ],
247+
name: [ 'kermit', 'fozzie' ]
248+
},
249+
headers: {
250+
X-Test: [ 'Test Form Parameters Value' ],
251+
Content-Type: [ 'application/x-www-form-urlencoded' ]
252+
}
253+
}
254+
""");
255+
assertThat(response.getStatusCode()).isEqualTo(200);
256+
assertThat(response.getHttpHeaders().get("Content-Type"))
257+
.containsExactly("application/json");
258+
}
259+
227260
@ParameterizedTest
228261
@ArgumentsSource(FlowableHttpClientArgumentProvider.class)
229262
void deleteWithoutBody(FlowableHttpClient httpClient) {

0 commit comments

Comments
 (0)