当前位置: 首页 > 知识库问答 >
问题:

Webflux Spring 2.1.2自定义内容类型

邵飞白
2023-03-14

我正试图通过WebClient发布以获取microsoft令牌:

public WebClient getWebclient() {
    TcpClient client = TcpClient.create()
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
            .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(15)).addHandlerLast(new WriteTimeoutHandler(15)));

    ExchangeStrategies strategies = ExchangeStrategies.builder()
            .codecs(configurer -> {
                configurer.registerDefaults(true);
                FormHttpMessageReader formHttpMessageReader = new FormHttpMessageReader();
                formHttpMessageReader.setEnableLoggingRequestDetails(true);
                configurer.customCodecs().reader(formHttpMessageReader);
            })
            .build();

    return WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(HttpClient.from(client).followRedirect(true)))
            .exchangeStrategies(strategies)
            .filter(logRequest())
            .filter(logResponse())
            .build();
}

MultiValueMap<String, String> credentials = new LinkedMultiValueMap<>();
        credentials.add("grant_type", "password");
        credentials.add("client_id", oauthClientId);
        credentials.add("resource", oauthResource);
        credentials.add("scope", oauthScope);
        credentials.add("username", oauthUsername);
        credentials.add("password", oauthPassword);

        Mono<MicrosoftToken> response = webClientService.getWebclient().post()
                .uri(oauthUrl)
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(credentials))
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                        Mono.error(new WebClientException(clientResponse.bodyToMono(String.class), clientResponse.statusCode())))
                .bodyToMono(MicrosoftToken.class);

        this.cachedToken = response.block();

问题在于,microsoft无法处理内容类型:application/x-www-form-urlencoded;charset=utf-8。

Spring会自动将charset=utf-8添加到请求中。我需要把这额外的字符集去掉。我需要一个内容类型:application/x-www-form-urlencoded。这可能吗?否则,我需要将我的spring版本降级到2.0.0,其中字符集不会自动添加。

2019-03-14 10:08:42 DEBUG [reactor.netty.channel.ChannelOperationsHandler]: 
[id: 0x5d6effce, L:/192.168.148.14:52285 - 
R:login.microsoftonline.de/51.4.136.42:443] Writing object 
DefaultHttpRequest(decodeResult: success, version: HTTP/1.1)
POST /common/oauth2/token HTTP/1.1
user-agent: ReactorNetty/0.8.4.RELEASE
host: login.microsoftonline.de
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 205
2019-03-14 10:08:42 DEBUG [reactor.netty.channel.ChannelOperationsHandler]: 
[id: 0x5d6effce, L:/192.168.148.14:52285 - 
R:login.microsoftonline.de/51.4.136.42:443] Writing object 

我用spring版本2.0.0对此进行了测试,并没有像新版本那样添加字符集:

POST /common/oauth2/token HTTP/1.1
user-agent: ReactorNetty/0.7.5.RELEASE
host: login.microsoftonline.de
accept-encoding: gzip
Content-Type: application/x-www-form-urlencoded
Content-Length: 205

共有1个答案

姚正真
2023-03-14

我花了整整一个上午的时间才弄明白,但我终于设法做到了。问题是,Webfluxbodyinserters.fromformdata总是将内容类型设置为application/x-www-form-urlencoded;charset=...而不管您在头中设置了什么。

要解决这一点,首先定义这个方法:

/**
 * This method is unfortunately necessary because of Spring Webflux's propensity to add {@code ";charset=..."}
 * to the {@code Content-Type} header, which the Generic Chinese Device doesn't handle properly.
 *
 * @return a {@link FormInserter} that doesn't add the character set to the content type header
 */
private FormInserter<String> formInserter() {

    return new FormInserter<String>() {

        private final MultiValueMap<String, String> data = new LinkedMultiValueMap<>();

        @Override public FormInserter<String> with(final String key, final String value) {
            data.add(key, value);
            return this;
        }

        @Override public FormInserter<String> with(final MultiValueMap<String, String> values) {
            data.addAll(values);
            return this;
        }

        @Override public Mono<Void> insert(final ClientHttpRequest outputMessage, final Context context) {
            final ResolvableType formDataType =
                    ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class);
            return new FormHttpMessageWriter() {
                @Override protected MediaType getMediaType(final MediaType mediaType) {
                    if (MediaType.APPLICATION_FORM_URLENCODED.equals(mediaType)) {
                        return mediaType;
                    } else {
                        return super.getMediaType(mediaType);
                    }
                }
            }.write(Mono.just(this.data), formDataType,
                    MediaType.APPLICATION_FORM_URLENCODED,
                    outputMessage,
                    context.hints());
        }
    };
}

然后,要调用web服务,请执行以下操作:

final SomeResponseObject response = WebClient
        .builder()
        .build()
        .post()
        .uri(someOrOtherUri)
        .body(formInserter().with("param1", "value1")
                            .with("param2", "value2")
        )
        .retrieve()
        .bodyToFlux(SomeReponseObject.class)
        .blockLast();
 类似资料:
  • 我一直在尝试实现RESTFul体系结构,但我完全搞不清自定义媒体类型是好是坏。 目前,我的应用程序使用Http-Link:header传递“链接”。这很好,我将其与title属性一起使用,允许服务器描述这个“动作”到底是什么,尤其是当呈现给用户时。 我感到困惑的是是否应该指定自定义mime类型。例如,我有一个用户的概念。它可能与当前资源相关联。我要编一个例子,说我有一件拍卖品。我们可能有一个用户在

  • 我使用的是spring Boot2.1.6,Release and swagger 2.9.2,除了我想简化内容之外,一切都很好。 首先,我想删除标题下的: 而且,我希望和在访问时打开,直到我单击名称。

  • 基础 content 渲染 <vuep template="#exampleContent"></vuep> <template> <div class="amap-page-container"> <el-amap vid="amapDemo" :center="center" :zoom="zoom" class="amap-d

  • 我是thymeleaf的新手,我正试图创建一个web组件。我想要的是这样的东西: 向路易斯问好

  • 基础 content 渲染 <vuep template="#exampleContent"></vuep> <script v-pre type="text/x-template" id="exampleContent"> <template> <div class="amap-page-container"> <el-amap vid="amapDe

  • 如何使用Apache Thrift编写自定义容器类型? 我知道,在默认情况下,Thrift中的任何map声明都会扩展HashMap。 是否有一种方法可以修改它以扩展ConvoltHashMap而不是HashMap? 例如,if测试。节俭的定义如下: 结构测试数据{1:字符串id,2:地图详细信息} 如何确保此映射作为ConcurrentHashMap实现? 谢谢