Reputation: 10949
I'm trying to send a POST request using the new http client api.
Is there a built in way to send parameters formatted as x-www-form-urlencoded
?
My current code:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(BodyPublishers.ofString("a=get_account&account=" + URLEncoder.encode(account, "UTF-8")))
.build();
What I'm looking is for a better way to pass the parameters. Something like this:
Params p=new Params();
p.add("a","get_account");
p.add("account",account);
Do I need to build myself this functionality or is something already built in?
I'm using Java 12.
Upvotes: 42
Views: 34323
Reputation: 528
I think the following is the best way to achieve this using Java 11:
Map<String, String> parameters = new HashMap<>();
parameters.put("a", "get_account");
parameters.put("account", account);
String form = parameters.entrySet()
.stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(form))
.build();
HttpResponse<?> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body().toString());
Upvotes: 43
Reputation: 2374
Here is a helper method for converting a Map
into a URL encoded form-data payload:
private static String getFormDataAsString(Map<String, String> formData) {
var formBodyBuilder = new StringBuilder();
for (Map.Entry<String, String> singleEntry : formData.entrySet()) {
if (formBodyBuilder.length() > 0) {
formBodyBuilder.append("&");
}
formBodyBuilder.append(URLEncoder.encode(singleEntry.getKey(), StandardCharsets.UTF_8));
formBodyBuilder.append("=");
formBodyBuilder.append(URLEncoder.encode(singleEntry.getValue(), StandardCharsets.UTF_8));
}
return formBodyBuilder.toString();
}
You can easily use it like so:
var formData = Map.of(
"key-1", "value-1",
"key-2", "value-2",
"key-3", "value-3"
);
var request = HttpRequest.newBuilder()
.uri(requestUri)
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(getFormDataAsString(formData)))
.build();
Additional gotchas:
POST
or PUT
as the HTTP method verb, because most servers will ignore the body on GET
requests.Upvotes: 1
Reputation: 41
Instead of Stream.of you can use more compact String.join (according to Łukasz Olszewski answer):
String form = Map.of("param1", "value1", "param2", "value2")
.entrySet()
.stream()
.map(entry -> String.join("=",
URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8),
URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8)))
.collect(Collectors.joining("&"));
return HttpRequest.BodyPublishers.ofString(form);
Upvotes: 3
Reputation: 522
Check out Methanol. It's got a nice FormBodyPublisher
for x-www-form-urlencoded
bodies.
var formBody = FormBodyPublisher.newBuilder()
.query("a", "get_account")
.query("account", account)
.build();
var request = MutableRequest.POST("https://example.com", formBody);
// Methanol implements an HttpClient that does nice things to your request/response.
// Here, the Content-Type header will be added for you.
var client = Methanol.create();
var response = client.send(request, BodyHandlers.ofString());
Upvotes: 5
Reputation: 49
As Łukasz Olszewski said , worked correctly :
String params = Map.of(
Constants.PARAM_CLIENT_ID, apiObject.getClientId(),
Constants.PARAM_SCOPE, apiObject.getScope(),
Constants.PARAM_CODE, apiObject.getCode(),
Constants.PARAM_REDIRECT_URI, apiObject.getRedirectUri(),
Constants.PARAM_GRANT_TYPE, apiObject.getGrantType(),
Constants.PARAM_CODE_VERIFIER, apiObject.getCodeVerifier())
.entrySet()
.stream()
.map(entry -> Stream.of(
URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8),
URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("="))
).collect(Collectors.joining("&"));
HttpResponse<?> response = utils.consumeHttpPostFormUrlEncodedClientByRequestUrl(Constants.URL_BASE + Constants.URL_GET_TOKEN, params);
and consumeHttpPostFormUrlEncodedClientByRequestUrl
public HttpResponse<?> consumeHttpPostFormUrlEncodedClientByRequestUrl(String url, String map) throws IOException, InterruptedException {
HttpClient httpClient = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.header("Content-Type", String.valueOf(MediaType.APPLICATION_FORM_URLENCODED))
.POST(HttpRequest.BodyPublishers.ofString(map))
.build();
return httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}
Upvotes: 4
Reputation: 915
This way could be useful:
String param = Map.of("param1", "value1", "param2", "value2")
.entrySet()
.stream()
.map(entry -> Stream.of(
URLEncoder.encode(entry.getKey(), UTF_8),
URLEncoder.encode(entry.getValue(), UTF_8))
.collect(Collectors.joining("="))
).collect(Collectors.joining("&"));
You can use up to 10 pairs (param, value) by Map.of(...)
. It returns an unmodifiable map.
Upvotes: 5