Вызов Java Springboot WebClient к API приводит к http-415

Я работаю с загрузкой Java Spring и пытаюсь завершить интеграцию API подписок PayPal.

У меня есть план подписки на шоу, работающий в WebClient, хотя я не могу завершить функциональность для метода отмены подписки.

Я получаю ошибку 415 --

"message": "415 Unsupported Media Type from POST https://api.sandbox.paypal.com/v1/billing/subscriptions/I-JJ0B8HRKSRYV/cancel",

https://developer.paypal.com/api/rest/responses/

https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_cancel - когда я смотрю на подход curl - он утверждает, что content-type и accept находятся в заголовке - но это также может быть способ, которым я пытался добавить тело?

https://reflectoring.io/spring-webclient/

//метод showSubscriptionDetails

@CrossOrigin
@GetMapping("/api/showsubscriptiondetails")
@ResponseBody
public ResponseEntity<BaseResponse> showSubscriptionDetails(
        @RequestParam("subscriptionID") String subscriptionID
) throws PayPalRESTException {
    BaseResponse response = new BaseResponse();

    String bearerToken = generateAccessToken();
    String token = bearerToken.split(" ")[1].trim();

    WebClient client = WebClient.create(host);

    WebClient.ResponseSpec responseSpec = client.get()
            .uri("/v1/billing/subscriptions/"+subscriptionID+"?fields=plan")
            .headers(h -> {
                h.setBearerAuth(token);
            })
            .retrieve();

    String responseBody = responseSpec.bodyToMono(String.class).block();


    ObjectMapper mapper = new ObjectMapper();
    JsonNode root = null;
    try {
        root = mapper.readTree(responseBody);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    JsonNode status = root.path("status");
    JsonNode plan = root.path("plan").get("name");

    BasicDBObject res = new BasicDBObject();
    res.put("status", status);
    res.put("plan", plan);

    response.setCode("OK");
    response.setData(res);
    response.setDescription(appMessageLocalUtil.getLanguageMessage("subscription.found"));
    return ResponseEntity.ok().body(response);
}

//метод отмены подписки

MultiValueMap<String, String> bodyValues = new LinkedMultiValueMap<>();
bodyValues.add("reason", "too expensive");


String bearerToken = generateAccessToken();
String token = bearerToken.split(" ")[1].trim();

WebClient client = WebClient.create(host);

WebClient.ResponseSpec responseSpec = client.post()
        .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
        .headers(h -> {
            h.setBearerAuth(token);
        })
        .retrieve();

String responseBody = responseSpec.bodyToMono(String.class).block();

System.out.println("responseSpec-- "+ responseSpec);
System.out.println("responseBody-- "+ responseBody);

... попытки

Я пытался добавить тип контента в заголовок и как часть основной сборки, но, похоже, ничего не проходит.

       WebClient client = WebClient.create(host);

        WebClient.ResponseSpec responseSpec = client.post()
                .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
                .headers(h -> {
                    h.setBearerAuth(token);
                    //h.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                    //h.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
                })
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                //.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                //.contentType(MediaType.APPLICATION_FORM_URLENCODED)
                //.accept(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromFormData(bodyValues))
                .retrieve();

-- 21 апреля 2023 г.

рабочий код был таким: создание тела объекта и использование fromValue в bodyinserter - responseBody имеет значение null в результате, но он отменяет идентификатор подписки

BasicDBObject body = new BasicDBObject();
body.put("reason", "xxx");

String bearerToken = generateAccessToken();
String token = bearerToken.split(" ")[1].trim();

WebClient client = WebClient.create(host);

WebClient.ResponseSpec responseSpec = client.post()
        .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
        .headers(h -> {
            h.setBearerAuth(token);
        })
        .contentType(MediaType.APPLICATION_JSON)
        .body(BodyInserters.fromValue(body))
        .retrieve();

String responseBody = responseSpec.bodyToMono(String.class).block();
System.out.println("responseSpec-- "+ responseSpec);
System.out.println("responseBody-- "+ responseBody);
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В API-документе, на который вы ссылаетесь, говорится, что тело отмены — это json и содержит {«причина»: «слишком дорого»}. Вы отправляете форму. Отсюда 415: форма ≠ json.

Попробуй это:

String body = “{\”reason\”: \”too expensive\”}”;

WebClient client = WebClient.create(host);

        WebClient.ResponseSpec responseSpec = client.post()
   .uri("/v1/billing/subscriptions/"+subscriptionID+"/cancel")
            .headers(h -> {
                h.setBearerAuth(token);
            })
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromValue(body))
            .retrieve();

это возвращает ошибку 400 -- 400 Bad Request от POST

The Old County 21.04.2023 17:53

Не более 415, поэтому я ответил на ваш вопрос. Может быть, идентификатор подписки шаткий ?? Добавьте код, чтобы получить тело ответа 400, распечатайте его и добавьте в новый вопрос.

John Williams 21.04.2023 17:56

У меня это заработало, если я изменил тело на объект -- BasicDBObject body = new BasicDBObject(); body.put ("причина", "xxx");

The Old County 21.04.2023 17:57

.contentType(MediaType.APPLICATION_JSON)

The Old County 21.04.2023 17:58

Я отредактировал свой ответ, чтобы также исправить 400.

John Williams 21.04.2023 18:04

Тело строки = "{\"причина\": \"слишком дорого\"}"; -- это работает, но это кажется беспорядочным способом создания объекта

The Old County 21.04.2023 18:36

Согласитесь, я бы определил класс с полем «причина» и разместил бы его, но такой формат не особо облегчает. Мое внимание сосредоточено на первоначальном вопросе, то есть разрешении 415 (а затем 400.

John Williams 21.04.2023 18:41

Можете ли вы поделиться документацией, которая привела вас к этому решению - я не нашел много примеров.

The Old County 21.04.2023 18:52

У меня большой опыт - я видел много ошибок 415, и они неизменно возникают, потому что форма была отправлена, но ожидался json или наоборот. В API-документе было довольно ясно о типе контента, я пропустил структуру полезной нагрузки поста, в основном потому, что это немного глупо — класс с одним полем. Одно поле может быть опубликовано. Поиск в Google «что вызывает ответ http 415» поможет вам довольно близко

John Williams 21.04.2023 19:00

Я имею в виду то, что примеры, которые я нашел, указывали, что тело должно быть MultiValueMap и завернуто в fromFormData - я не мог видеть четкого примера, который предлагал бы только fromValue.

The Old County 21.04.2023 19:28

MultiValueMap/fromFormData предназначен для серверной части, принимающей данные из html-форм. API с гораздо большей вероятностью примет json. Несколько примеров имплантации — codersee.com/spring-5-webclient-with-spring-boot и этот вопрос stackoverflow.com/questions/62073018/….

John Williams 21.04.2023 19:47

Другие вопросы по теме

В Java начальная зависимость Azure applicationinsights несовместима с весенней загрузкой 3.x.x
H2 не может получить Instant при использовании собственного запроса [Spring Boot 3]
Как я могу заставить пользовательский интерфейс Swagger запрашивать у меня заголовок авторизации вместо моей сущности пользователя?
SpringBoot 3 JDK 17 — несоответствие ClassLoader для [java.io.FileWriter]
Сбой сборки Spring Native с GCP
Запрос GraphQL с вводом ненулевого типа объекта возвращает внутреннюю ошибку, поскольку он превратился в нуль при проверке в коде
Не удается подключиться к брокеру Kafka с помощью Spring Boot
Обертка REST API в методы CRUD базы данных Spring, чтобы его можно было использовать в качестве @Repository
Атрибут 'url' не указан, и встроенный источник данных не может быть настроен. Не удалось определить подходящий класс водителя
Сборка приложения Spring Boot развернута/собирается успешно, но происходит сбой из-за отсутствия основного атрибута манифеста в .jar (Maven)

Похожие вопросы

Как я могу получить имя компонента пользовательского интерфейса, чтобы я мог устанавливать и получать значения из и в БД?
Как запретить allure-junit5 генерировать отчеты для тестов Cucumber7?
Создайте JSON, совместимый с jakarta swagger-ui, из исходного кода, используя maven
Как отправить и вычесть сообщение в очередь ActiveMQ Artemis через JMX?
Сократите поток до одного объекта без создания нескольких объектов и явной мутации
В Java начальная зависимость Azure applicationinsights несовместима с весенней загрузкой 3.x.x
Функция setVisible(false) для JFrame не приводит к исчезновению JFrame
Запрос GET с URL-адресом перенаправления не может получить 302 в JAVA, но Postman работает хорошо
Как я могу заставить пользовательский интерфейс Swagger запрашивать у меня заголовок авторизации вместо моей сущности пользователя?
Как заставить функцию CompletableFuture исключительно возвращать тип коллекции?