okhttp add post params from array - java

im trying to send post request with N numbers of params using okhttp3.
i can only find a way doing it with predefined number of params.
how can i dynamically add more params to the post request?
i want to loop over an array list of keys and values and add them to the addFormDataPart or any other equivalent method.
this is the current code:
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("email", "your-email#email.com")
.addFormDataPart("name", "your-name")
.build();
Request request = new Request.Builder()
.url("url")
.post(requestBody)
.build();

use a Hash map
// HashMap with Params
HashMap<String, String> params = new HashMap<>();
// dynamically add more parameters like this:
params.put( "name", "Arom" );
params.put( "key_from_list", "value_from_list" );
// Initialize Builder (not RequestBody)
FormBody.Builder builder = new FormBody.Builder();
// loop while Adding Params to Builder
for ( Map.Entry<String, String> entry : params.entrySet() ) {
builder.add( entry.getKey(), entry.getValue() );
}
// Create RequestBody
RequestBody formBody = builder.build();
// Create Request (same)
Request request = new Request.Builder()
.url( "url" )
.post( formBody )
.build();

Related

Java 11: New HTTP client send POST requests with x-www-form-urlencoded parameters

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.
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))
.headers("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());
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.
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());
}
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);
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());

Sending POST request with body in netty

I want to do POST request to some API by netty. Request must contains parameters as form-data in body. How I try to do this:
FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, POST, url);
httpRequest.setUri("https://url.com/myurl");
ByteBuf byteBuf = Unpooled.copiedBuffer(myParameters, Charset.defaultCharset());
httpRequest.headers().set(ACCEPT_ENCODING, GZIP);
httpRequest.headers().set(CONTENT_TYPE, "application/json");
httpRequest.headers().set(CONTENT_LENGTH, byteBuf.readableBytes());
httpRequest.content().clear().writeBytes(byteBuf);
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CNXN_TIMEOUT_MS)
.handler(new ChannelInitializerCustomImpl());
ChannelFuture cf = b.connect(url.getHost(), port);
cf.addListener(new ChannelFutureListenerCustomImpl();
That's worked ok, but result is different from I received by postman or other instruments.
What's the correct way to set my parameters as form-data to request body?
I solved this problem by using Apache httpcomponents library to create HtppEntity, serialize it to byte array and set to netty ByteBuf and also use jackson for parse json from String to Map:
Map<String, String> jsonMapParams = objectMapper.readValue(jsonStringParams, new TypeReference<Map<String, String>>() {});
List<NameValuePair> formParams = jsonMapParams.entrySet().stream()
.map(e -> new BasicNameValuePair(e.getKey(), e.getValue()))
.collect(Collectors.toList());
HttpEntity httpEntity = new UrlEncodedFormEntity(formParams);
ByteBuf byteBuf = Unpooled.copiedBuffer(EntityUtils.toByteArray(httpEntity));
httpRequest.headers().set(ACCEPT_ENCODING, GZIP);
httpRequest.headers().set(CONTENT_TYPE, "application/x-www-form-urlencoded");
httpRequest.headers().set(CONTENT_LENGTH, byteBuf.readableBytes());
httpRequest.content().clear().writeBytes(byteBuf);
I think that your request header is not set correct, to set content_type to "application/x-www-form-urlencoded" and have a try.

Adding multiple headers while calling REST api via Jersey clients

I am trying to add multiple header. But failed miserably so far. I had tried lots of code tweaking but failed. Can someone help me fix the code or at least tell me what's wrong ?
Header mapping code:
Map<String, String> headers = new HashMap<String, String>();
headers.put("authorization", authToken);
headers.put("API-Version", apiVersion);
headers.put("Content-Type", MediaType.APPLICATION_JSON);
actual calling code:
String serviceUrl = serviceHostUrl;
Client client = Client.create();
WebResource webResource = client.resource(serviceUrl).path(path);
WebResource.Builder builder = webResource.getRequestBuilder();
if(headers != null && !headers.isEmpty()) {
for(Map.Entry<String, String> entry : headers.entrySet()) {
builder.header(entry.getKey(), entry.getValue());
}
}
ClientResponse response = builder.post(ClientResponse.class, input);
UPDATE
if in second snippet I use below code instead of setting headers in loop, it works fine. That's really weird.
builder.header("authorization", "Basic SDFSFSDFSDFSDFSDFSDFSDF");
builder.header("API-Version", "5.2");
builder.header("Content-Type", MediaType.APPLICATION_JSON);
I think the issue here is, type of MAP that you are trying to access.
Map<String, Object> headers = new HashMap<String, Object>();
The WebSource builder accepts header(String,Object). So Try with changing the map type.
The header method does change the builder object. It creates a new object and the existing builder is not affected.
for(Map.Entry<String, String> entry : headers.entrySet()) {
builder = builder.header(entry.getKey(), entry.getValue());
}
Basic auth is like: Authorization = "Authorization" ":" credentials
an example
byte[] loginBytes = ("1" + ":" + "1").getBytes();
StringBuilder authString = new StringBuilder().append("Basic ")
.append(Base64.encodeToString(loginBytes, Base64.NO_WRAP));
_headers.put("Authorization", authString );

How to post array parameters with HttpComponents

I want to perform this command with Apache http-components (4.1.2)
curl --data "strings[]=testOne&string-keys[]=test.one&strings[]=testTwo&string-keys[]=test.two&project=Test" https://api.foo.com/1/string/input-bulk
The target api need strings and string-keys parameters as array, which mean repeating strings[] and string-keys[] for each parameter.
This curl command works fine but with Http-component, while I got exactly the same parameters.
Maybe I'm doing something wrong.
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add( new BasicNameValuePair( "project", PROJECT_NAME ) );
for ( Entry entry : newEntries )
{
params.add( new BasicNameValuePair( "string-keys[]", entry.getKey() ) );
params.add( new BasicNameValuePair( "strings[]", entry.getValue() ) );
params.add( new BasicNameValuePair( "context[]", "" ) );
}
URI uri = URIUtils.createURI( "https", "api.foo.com", -1, "/1/string/input-bulk", null, null );
UrlEncodedFormEntity paramEntity = new UrlEncodedFormEntity( params );
logger.info( "POST params : {}", EntityUtils.toString( paramEntity ) );
HttpPost httpRequest = new HttpPost( uri );
httpRequest.setEntity( paramEntity );
HttpResponse response = new DefaultHttpClient().execute( httpRequest );
The POST params looks like :
POST params : project=Test&string-keys%5B%5D=test.one&strings%5B%5D=TestOne&string-keys%5B%5D=test.two&strings%5B%5D=TestTwo
If I put them behind --data in curl, it works, but not with HttpCoponents.
Can someone explain me why?
Thanks in advance
Try adding the header "application/x-www-form-urlencoded" in your httpRequest
httpRequest.addHeader("content-type", "application/x-www-form-urlencoded");
HttpResponse response = new DefaultHttpClient().execute( httpRequest );
Hopefully that will work

Using the Jersey client to do a POST operation

In a Java method, I'd like to use a Jersey client object to do a POST operation on a RESTful web service (also written using Jersey) but am not sure how to use the client to send the values that will be used as FormParam's on the server. I'm able to send query params just fine.
Not done this yet myself, but a quick bit of Google-Fu reveals a tech tip on blogs.oracle.com with examples of exactly what you ask for.
Example taken from the blog post:
MultivaluedMap formData = new MultivaluedMapImpl();
formData.add("name1", "val1");
formData.add("name2", "val2");
ClientResponse response = webResource
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
.post(ClientResponse.class, formData);
That any help?
Starting from Jersey 2.x, the MultivaluedMapImpl class is replaced by MultivaluedHashMap. You can use it to add form data and send it to the server:
WebTarget webTarget = client.target("http://www.example.com/some/resource");
MultivaluedMap<String, String> formData = new MultivaluedHashMap<String, String>();
formData.add("key1", "value1");
formData.add("key2", "value2");
Response response = webTarget.request().post(Entity.form(formData));
Note that the form entity is sent in the format of "application/x-www-form-urlencoded".
It is now the first example in the Jersey Client documentation
Example 5.1. POST request with form parameters
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:9998").path("resource");
Form form = new Form();
form.param("x", "foo");
form.param("y", "bar");
MyJAXBBean bean =
target.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(form,MediaType.APPLICATION_FORM_URLENCODED_TYPE),
MyJAXBBean.class);
If you need to do a file upload, you'll need to use MediaType.MULTIPART_FORM_DATA_TYPE.
Looks like MultivaluedMap cannot be used with that so here's a solution with FormDataMultiPart.
InputStream stream = getClass().getClassLoader().getResourceAsStream(fileNameToUpload);
FormDataMultiPart part = new FormDataMultiPart();
part.field("String_key", "String_value");
part.field("fileToUpload", stream, MediaType.TEXT_PLAIN_TYPE);
String response = WebResource.type(MediaType.MULTIPART_FORM_DATA_TYPE).post(String.class, part);
Simplest:
Form form = new Form();
form.add("id", "1");
form.add("name", "supercobra");
ClientResponse response = webResource
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
.post(ClientResponse.class, form);
Also you can try this:
MultivaluedMap formData = new MultivaluedMapImpl();
formData.add("name1", "val1");
formData.add("name2", "val2");
webResource.path("yourJerseysPathPost").queryParams(formData).post();

Categories

Resources