Что не так с моим запросом HttpURLConnection? - PullRequest
0 голосов
/ 11 апреля 2019

Я пытаюсь вызвать REST API с помощью запроса PUT, но получаю код ошибки 400 (неверный запрос). Может кто-то заметит, что я могу делать не так?

Я успешно вызвал этот API с клиентом REST, вот используемые заголовки и тело:

https://imgur.com/dZVyawn https://imgur.com/lMtn2JB

    String credentials = Base64.getEncoder().encodeToString(("wcadmin:wcadmin").getBytes());
    URL url = new URL(getURL());
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("PUT");
    connection.setDoOutput(true);
    connection.setDoInput(true);

    //Set Headers
    String fileUrl = "c:\\0000000050.xml";
    File fileToUpload = new File(fileUrl);
    long length = fileToUpload.length();
    String FORM_DATA_BOUNDARY = "------FormBoundary" + System.currentTimeMillis();
    connection.setRequestProperty("csrf_nonce", getNonceValue());
    connection.setRequestProperty("Accept", "application/xml");
    connection.setRequestProperty("Authorization", "Basic " + credentials);
    connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + FORM_DATA_BOUNDARY);
    connection.setRequestProperty("Content-Length", Long.toString(length));

    //Setup Request Body Writer
    OutputStream requestBodyOutputStream = connection.getOutputStream();
    BufferedWriter requestBodyWriter = new BufferedWriter(new OutputStreamWriter(requestBodyOutputStream));

    //Write Body
    requestBodyWriter.write("\r\n\r\n");
    requestBodyWriter.write(FORM_DATA_BOUNDARY);
    requestBodyWriter.write("\r\n");
    requestBodyWriter.write("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileUrl + "\"");
    requestBodyWriter.write("\r\n");
    requestBodyWriter.write("Content-Type: text/xml");
    requestBodyWriter.write("\r\n\r\n");
    requestBodyWriter.flush();

    FileInputStream uploadFileStream = new FileInputStream(fileToUpload);
    int bytesRead;
    byte[] dataBuffer = new byte[1024];
    while ((bytesRead = uploadFileStream.read(dataBuffer)) != -1) {
        requestBodyOutputStream.write(dataBuffer, 0, bytesRead);
    }
    requestBodyOutputStream.flush();

    requestBodyWriter.write("\r\n");
    requestBodyWriter.write(FORM_DATA_BOUNDARY);
    requestBodyWriter.flush();

    //Close the streams
    requestBodyOutputStream.close();
    requestBodyWriter.close();
    uploadFileStream.close();

    //Read Response
    String inputLine;
    StringBuffer content = new StringBuffer();
    InputStream inputStream = connection.getInputStream();
    if (inputStream != null) {
        BufferedReader responseReader = new BufferedReader(new InputStreamReader(inputStream));
        if (responseReader != null) {
            while ((inputLine = responseReader.readLine()) != null) {
                content.append(inputLine);
            }
            responseReader.close();
        }
    }

    connection.disconnect();

Ошибка 400 Получен неправильный ответ на запрос

1 Ответ

0 голосов
/ 11 апреля 2019

Первое и самое важное: вы не можете записывать как в OutputStream, так и в OutputStreamWriter, который оборачивает тот же OutputStream. Они будут конфликтовать друг с другом.

Не использовать OutputStreamWriter вообще; вместо этого преобразуйте текст в байты самостоятельно:

OutputStream requestBodyOutputStream = connection.getOutputStream();

requestBodyOutputStream.write("\r\n\r\n".getBytes(StandardCharsets.UTF_8));
requestBodyOutputStream.write(FORM_DATA_BOUNDARY.getBytes(StandardCharsets.UTF_8));
// etc.

Во-вторых, вы конвертируете между байтами и строками, используя системную кодировку по умолчанию, что означает, что именно то, что записывается, зависит от системы, в которой выполняется код. Не вызывайте String.getBytes без указания явного Charset. Обычно getBytes(StandardCharsets.UTF_8) - это то, что вы хотите.

Точно так же вам нужно передать Charset в ваше создание InputStreamReader (хотя это не является причиной вашей проблемы, так как вы не получаете правильный ответ в данный момент). Не берите кодировку; используйте параметр типа charset MIME из заголовка ответа Content-Type . Если вы используете версию Java старше 11, вы можете проанализировать значение Content-Type с классом javax.activation.MimeType , но имейте в виду, что пакет javax.activation был удален из Java SE с Java 11. Для Java 11 и более поздних версий Java Activation можно загрузить в качестве отдельной библиотеки. Другой вариант - использовать для анализа библиотеку JavaMail, в частности ее класс ContentType .

В-третьих, заголовок Content-Length внутри части тела (между границами) должен использовать длину файла в качестве Content-Length. Длина содержимого всего тела запроса должна быть длиной всего написанного: границ, заголовков частей тела и содержимого файла.

Хорошая новость заключается в том, что я думаю (хотя я не уверен), что URLConnection автоматически установит общую длину содержимого запроса, основываясь на байтах, которые вы пишете, поэтому вам, вероятно, не нужно вычислять длину самостоятельно; Вы можете просто воздержаться от установки «Content-Length» вообще.

Когда вы передадите правильный запрос, вы обнаружите, что отбрасываете новые строки в ответе. Если предполагается, что ответом является текст, читаемый человеком, эти новые строки, вероятно, будут иметь значение. Если вы используете Java 10 или более позднюю версию, вы можете использовать Reader.transferTo с StringWriter:

StringWriter responseBody = new StringWriter();
responseReader.transferTo(responseBody);
String content = responseBody.toString();

Если вы используете версию Java старше 10:

  • new BufferedReader не может вернуть ноль, поэтому проверка на ноль бессмысленна. В Java оператор new всегда, несмотря ни на что, возвращает новый объект (если не выдается исключение, в этом случае new вообще не возвращает).
  • Вы должны использовать StringBuilder, а не StringBuffer. Они идентичны, за исключением того, что StringBuffer является более старым классом, который обеспечивает безопасность потоков для каждого метода, создавая ненужные накладные расходы почти для всех случаев использования.

Вы копируете свой файл в запрос без какой-либо буферизации, что будет медленным и неэффективным. Попробуйте вместо этого использовать Files.copy(fileToUpload.toPath(), requestBodyOutputStream).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...