How to define multiple parameters for a POST request using Java 11 HTTP Client
Asked Answered
A

2

15

I have a code that makes a POST request for a specific endpoint. This code is using Apache's HttpClient and I would like to start using the native HttpClient from Java (JDK11). But I didn't understand how to specify the parameters of my request.

This is my code using Apache Httpclient:

var path = Path.of("file.txt");
var entity = MultipartEntityBuilder.create()
            .addPart("file", new FileBody(path.toFile()))
            .addTextBody("token", "<any-token>")
            .build();

And the code using HttpClient:

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
                         .uri(URI.create("https://myendpoint.com/"))
                         .POST( /* How can I set the parameters here? */ );

How can I set file and token parameters?

Ashil answered 6/6, 2019 at 16:14 Comment(3)
The Java 11 HttpClient doesn't appear to support file uploads. Also, it seems to require that you have all the POST arguments (which look identical to GET arguments) already set up somewhere instead of having a builder for them.Irwin
Having said that, someone else has already written a custom BodyPublisher to do fileuploads.Irwin
I am not very sure, but maybe try this out HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://myendpoint.com/")) .header("Content-Type", "multipart/form-data") .POST(HttpRequest.BodyPublishers.ofFile(Path.of("file.txt"))) .POST(HttpRequest.BodyPublishers.ofString("token")) .build();Fullrigged
M
17

Unfortunately the Java 11 HTTP client does not provide any convenient support for multipart kind of body. But we can build custom implementation on top of it:

Map<Object, Object> data = new LinkedHashMap<>();
data.put("token", "some-token-value");
data.put("file", File.createTempFile("temp", "txt").toPath());

// add extra parameters if needed

// Random 256 length string is used as multipart boundary
String boundary = new BigInteger(256, new Random()).toString();

HttpRequest.newBuilder()
              .uri(URI.create("http://example.com"))
              .header("Content-Type", "multipart/form-data;boundary=" + boundary)
              .POST(ofMimeMultipartData(data, boundary))
              .build();

public HttpRequest.BodyPublisher ofMimeMultipartData(Map<Object, Object> data,
                                                     String boundary) throws IOException {
        // Result request body
        List<byte[]> byteArrays = new ArrayList<>();

        // Separator with boundary
        byte[] separator = ("--" + boundary + "\r\nContent-Disposition: form-data; name=").getBytes(StandardCharsets.UTF_8);

        // Iterating over data parts
        for (Map.Entry<Object, Object> entry : data.entrySet()) {

            // Opening boundary
            byteArrays.add(separator);

            // If value is type of Path (file) append content type with file name and file binaries, otherwise simply append key=value
            if (entry.getValue() instanceof Path) {
                var path = (Path) entry.getValue();
                String mimeType = Files.probeContentType(path);
                byteArrays.add(("\"" + entry.getKey() + "\"; filename=\"" + path.getFileName()
                        + "\"\r\nContent-Type: " + mimeType + "\r\n\r\n").getBytes(StandardCharsets.UTF_8));
                byteArrays.add(Files.readAllBytes(path));
                byteArrays.add("\r\n".getBytes(StandardCharsets.UTF_8));
            } else {
                byteArrays.add(("\"" + entry.getKey() + "\"\r\n\r\n" + entry.getValue() + "\r\n")
                        .getBytes(StandardCharsets.UTF_8));
            }
        }

        // Closing boundary
        byteArrays.add(("--" + boundary + "--").getBytes(StandardCharsets.UTF_8));

        // Serializing as byte array
        return HttpRequest.BodyPublishers.ofByteArrays(byteArrays);
    }

Here's working example on Github (you need to change VirusTotal API key)

Menstruate answered 6/6, 2019 at 17:9 Comment(4)
Just asking BodyPublishers.ofMimeMultipartData(data) exists in Java-11? I was unable to find it in Java-12 at least.Fullrigged
@Fullrigged same here, I just found reference of ofMimeMultipartData here and here. Looks like a custom implementation, not part of JDK.Ashil
@Renan true, so the crux would be relying on BodyPublishers.ofByteArray(s) mostly.Fullrigged
Sorry guys, missed the part. Yes it's a custom implementation. The links provided by @Renan were correct. Updated the answer.Menstruate
F
0

The HttpClient doesn't provide any high level API to compose or format data in POST requests. You could either compose and format your post data manually, and then use either one of BodyPublishers.ofString(), BodyPublishers.ofInputStream(), or BodyPublishers.ofByteArrays() etc... to send it, or write your own implementation of BodyPublisher.

Fraud answered 7/6, 2019 at 12:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.