Spring WebFlux, как я могу отладить обмен сообщениями WebClient?

у меня возникли проблемы с пониманием того, что я сделал неправильно при построении моего запроса WebClient. Я хотел бы понять, как выглядит фактический HTTP-запрос. (например, сброс необработанного запроса на консоль)

POST /rest/json/send HTTP/1.1
Host: emailapi.dynect.net
Cache-Control: no-cache
Postman-Token: 93e70432-2566-7627-6e08-e2bcf8d1ffcd
Content-Type: application/x-www-form-urlencoded

apikey=ABC123XYZ&from=example%40example.com&to=customer1%40domain.com&to=customer2%40domain.com&to=customer3%40domain.com&subject=New+Sale+Coming+Friday&bodytext=You+will+love+this+sale.

Я использую реактивные инструменты Spring5 для создания API. У меня есть класс утилиты, который отправит электронное письмо с помощью api электронной почты Dyn. Я хотел бы использовать новый класс WebClient для этого (org.springframework.сеть.реактивный.функция.клиент.WebClient)

следующая команда была взята из:https://help.dyn.com/email-rest-methods-api/sending-api/#postsend

curl --request POST "https://emailapi.dynect.net/rest/json/send" --data "apikey=ABC123XYZ&from=example@example.com&to=customer1@domain.com&to=customer2@domain.com&to=customer3@domain.com&subject=New Sale Coming Friday&bodytext=You will love this sale."

когда я делаю вызов в curl с реальными значениями, электронная почта отправляет правильно, поэтому я чувствую, что генерирую свой запрос неправильно.

Отправка Команды

public Mono<String> send( DynEmailOptions options )
{
    WebClient webClient = WebClient.create();
    HttpHeaders headers = new HttpHeaders();
    // this line causes unsupported content type exception :(
    // headers.setContentType( MediaType.APPLICATION_FORM_URLENCODED );
    Mono<String> result = webClient.post()
        .uri( "https://emailapi.dynect.net/rest/json/send" )
        .headers( headers )
        .accept( MediaType.APPLICATION_JSON )
        .body( BodyInserters.fromObject( options ) )
        .exchange()
        .flatMap( clientResponse -> clientResponse.bodyToMono( String.class ) );
    return result;
}

Мой Класс DynEmailOptions

import java.util.Collections;
import java.util.Set;

public class DynEmailOptions
{
    public String getApikey()
    {
        return apiKey_;
    }

    public Set<String> getTo()
    {
        return Collections.unmodifiableSet( to_ );
    }

    public String getFrom()
    {
        return from_;
    }

    public String getSubject()
    {
        return subject_;
    }

    public String getBodytext()
    {
        return bodytext_;
    }

    protected DynEmailOptions(
        String apiKey,
        Set<String> to,
        String from,
        String subject,
        String bodytext
    )
    {
        apiKey_ = apiKey;
        to_ = to;
        from_ = from;
        subject_ = subject;
        bodytext_ = bodytext;
    }

    private Set<String> to_;
    private String from_;
    private String subject_;
    private String bodytext_;
    private String apiKey_;
}

2 ответов


в настоящее время вы пытаетесь сериализовать тело запроса "как есть", не используя right BodyInserter.

в этом случае, я думаю, что вы должны превратить ваш DynEmailOptions объект в MultiValueMap<String, String> и затем:

MultiValueMap<String, String> formData = ...
Mono<String> result = webClient.post()
                .uri( "https://emailapi.dynect.net/rest/json/send" )
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .accept( MediaType.APPLICATION_JSON )
                .body( BodyInserters.fromFormData(formData))
                .retrieve().bodyToMono(String.class);

вопрос заключается в отладке WebClient POST. Я нашел большую помощь в callicoder.com.

ключ должен добавить фильтр в WebClient. Фильтр обеспечивает легкий доступ как к запросам, так и к ответам. Для обоих запросов и ответов вы можете получить доступ к методу, URL, заголовкам и другим вещам. Однако вы не можете получить доступ к телу. Надеюсь, я ошибаюсь, но на самом деле существует только метод body() для установки тела.

здесь я должен жаловаться на странные поведение WebClient POST. Иногда, вместо того, чтобы получить ответ 4XX сразу, он блокирует навсегда. Иногда он дает 501 ответ. Мой совет: попробуйте использовать LinkedMultiValueMap для переноса тела, Избегайте использования простой строки или java.утиль.Карта.

вот мой пример кода, используя GitHub V3 API в качестве примера:

@Bean
public WebClient client() {
    return WebClient.builder()
        .baseUrl("https://api.github.com")
        .defaultHeader("User-Agent", "Spring-boot WebClient")
        .filter(ExchangeFilterFunctions.basicAuthentication("YOUR_GITHUB_USERNAME", "YOUR_GITHUB_TOKEN"))
        .filter(printlnFilter).build();
}
ExchangeFilterFunction printlnFilter= new ExchangeFilterFunction() {
    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        System.out.println("\n\n" + request.method().toString().toUpperCase() + ":\n\nURL:"
                + request.url().toString() + ":\n\nHeaders:" + request.headers().toString() + "\n\nAttributes:"
                + request.attributes() + "\n\n");

        return next.exchange(request);
    }
};
//In some method:
String returnedJSON = client.post().uri(builder->builder.path("/user/repos").build())
                .contentType(MediaType.APPLICATION_JSON)
                .syncBody(new LinkedMultiValueMap<String, String>(){{
                    put("name", "tett");
                }})
                .retrieve()
                .bodyToMono(String.class)
                .block(Duration.ofSeconds(3))

вы увидите такие вещи, как:

2018-04-07 12:15:57.823  INFO 15448 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8084
2018-04-07 12:15:57.828  INFO 15448 --- [           main] c.e.w.WebclientDemoApplication           : Started WebclientDemoApplication in 3.892 seconds (JVM running for 8.426)


POST:

URL:https://api.github.com/user/repos:

Headers:{Content-Type=[application/json], User-Agent=[Spring-boot WebClient], Authorization=[Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]}

Attributes:{}

есть 2 вещи, о которых нужно помнить: 1. Последовательность фильтров имеет значение. Поменять местами эти 2 фильтра и заголовок аутентификации не будет включен.
2. Фильтры фактически применяются ко всем запросам через этот экземпляр WebClient.

https://www.callicoder.com/spring-5-reactive-webclient-webtestclient-examples/ каждый полезно, может быть, вы должны прочитать его и скачать его пример кода.