返回顶部
首页 > 资讯 > 后端开发 > Python >WebClient抛UnsupportedMediaTypeException异常解决
  • 316
分享到

WebClient抛UnsupportedMediaTypeException异常解决

2024-04-02 19:04:59 316人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录前言问题背景问题分析解决方案方案一方案二方案三自定义解码器设置解码器方案四方案五方案六前言 前面分享了spring5中的WEBClient使用方法详解 后,就有朋友在s

前言

前面分享了spring5中的WEBClient使用方法详解 后,就有朋友在segmentfault上给博主提了一个付费的问题,这个是博主在segmentfault平台上面收到的首个付费问答,虽然酬劳不多,只有十元,用群友的话说性价比太低了。但在解决问题过程中对WebClient有了更深入的了解却是另一种收获。解决这个问题博主做了非常详细的排查和解决,现将过程记录在此,供有需要的朋友参考。

问题背景

使用WebClient请求一个接口,使用bodyToMono方法用一个Entity接收响应的内容,伪代码如下:

IdExocrResp resp = WebClient.create()
                .post()
                .uri("https://id.exocr.com:8080/bankcard")
                .body(BodyInserters.fromFORMData(formData))
                .retrieve()
                .bodyToMono(IdExocrResp.class)
                .block();

上面的代码在运行时会抛一个异常,异常如下:

Exception in thread "main" org.springframework.web.Reactive.function.UnsupportedMediaTypeException: Content type 'application/octet-stream' not supported for bodyType=IdExocrResp
	at org.springframework.web.reactive.function.BodyExtractors.lambda$readWithMessageReaders$12(BodyExtractors.java:201)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):

直译过来大概的意思就是,不支持application/octet-stream类型的Content Type。

问题分析

如上异常,抛异常的代码在BodyExtractors的201行,根据异常堆栈信息找到对应的代码分析:

private static  S readWithMessageReaders(
			ReactiveHttpInputMessage message, BodyExtractor.Context context, ResolvableType elementType,
			Function readerFunction,
			Function errorFunction,
			Supplier emptySupplier) {
		if (VOID_TYPE.equals(elementType)) {
			return emptySupplier.get();
		}
		MediaType contentType = Optional.ofNullable(message.getHeaders().getContentType())
				.orElse(MediaType.APPLICATION_OCTET_STREAM);
		return context.messageReaders().stream()
				.filter(reader -> reader.canRead(elementType, contentType))
				.findFirst()
				.map(BodyExtractors::cast)
				.map(readerFunction)
				.orElseGet(() -> {
					ListmediaTypes = context.messageReaders().stream()
							.flatMap(reader -> reader.getReadableMediaTypes().stream())
							.collect(Collectors.toList());
					return errorFunction.apply(
							new UnsupportedMediaTypeException(contentType, mediaTypes, elementType));
				});
	}

可以看到,在这个body提取器类中,有一个默认的contentType 策略,如果server端没有返回contentType ,默认就使用APPLICATION_OCTET_STREAM来接收数据。问题正是这里导致的。因为在这个接口的响应header里,contentType 为null,其实正确的应该是application/JSON,只是服务器没指定,然后被默认策略设置为application/octet-stream后,在默认的jsON解码器里是不支持,导致抛出了不支持的MediaType异常。定位到真实原因后,博主给出了如下方案

解决方案

方案一

如果服务端是自己的服务,可以修改服务端的程序指定ContentType为application/json类型返回即可。如果是第三方的服务,没法改动server端请参考下面的方案

方案二

使用String接收后,然后在flatMap里在过滤自己解码一遍,String类型可以接收application/octet-stream类型的Content Type的,代码如:

IdExocrResp resp = WebClient.create()
                .post()
                .uri("xxx")
                .body(BodyInserters.fromFormData(formData))
                .retrieve()
                .bodyToMono(String.class)
                .flatMap(str -> Mono.just(JSON.parseObject(str, IdExocrResp.class)))
                .block();

方案三

因为响应的值确实是json,只是在响应的header里没有指定Content Type为application/json。而最终异常也是因为json解码器不支持导致的,所以我们可以定制json解码器,重写支持的MediaType校验规则

自定义解码器


public class CustomJacksonDecoder extends AbstractJackson2Decoder {
    public CustomJacksonDecoder() {
        super(Jackson2ObjectMapperBuilder.json().build());
    }
    
    @Override
    protected boolean supportsMimeType(MimeType mimeType) {
        return (mimeType == null
                || mimeType.equals(MediaType.APPLICATION_OCTET_STREAM)
                || super.getDecodableMimeTypes().stream().anyMatch(m -> m.isCompatibleWith(mimeType)));
    }
}

设置解码器

ExchangeStrategies strategies = ExchangeStrategies.builder()
                .codecs(configurer -> configurer.customCodecs().decoder(new CustomJacksonDecoder()))
                .build();
        MultiValueMap formData = new LinkedMultiValueMap<>();
        IdExocrResp resp = WebClient.builder()
                .exchangeStrategies(strategies)
                .build()
                .post()
                .uri("https://id.exocr.com:8080/bankcard")
                .body(BodyInserters.fromFormData(formData))
                .retrieve()
                .bodyToMono(IdExocrResp.class)
                .block();

方案四

因为响应的DefaultClientResponse里没有Content-Type,所以可以使用exchange()拿到clientResponse后重新build一个ClientResponse,然后设置Content-Type为application/json即可解决问题,代码如:

MultiValueMap formData = new LinkedMultiValueMap<>();
        IdExocrResp resp = WebClient.create()
                .post()
                .uri("https://id.exocr.com:8080/bankcard")
                .body(BodyInserters.fromFormData(formData))
                .exchange()
                .flatMap(res -> ClientResponse.from(res)
                        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .body(res.body(BodyExtractors.toDataBuffers()))
                        .build()
                        .bodyToMono(IdExocrResp.class))
                .block();

方案五

同方案四的思路,重新构造一个带Content-Type为application/json的clientResponse,但是处理逻辑是在filter里,就不需要使用exchange()了,博主以为这种方式最简洁优雅,代码如:

MultiValueMap formData = new LinkedMultiValueMap<>();
        IdExocrResp resp = WebClient.builder()
                .filter((request, next) ->
                        next.exchange(request).map(response -> {
                            Fluxbody = response.body(BodyExtractors.toDataBuffers());
                            return ClientResponse.from(response)
                                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                                    .body(body)
                                    .build();
                        }))
                .build()
                .post()
                .uri("https://id.exocr.com:8080/bankcard")
                .body(BodyInserters.fromFormData(formData))
                .retrieve()
                .bodyToMono(IdExocrResp.class)
                .block();

方案六

前面原因分析的时候已经说了,MediaType为空时spring默认设置为application/octet-stream了。这里的设计其实可以更灵活点的,比如除了默认的策略外,还可以让用户自由的设置默认的Content Type类型。这个就涉及到改动Spring的框架代码了,博主已经把这个改动提交到Spring的官方仓库了,如果合并了的话,就可以在下个版本使用这个方案解决问题了

pr地址:https://GitHub.com/spring-projects/spring-framework/pull/24120

以上就是WebClient抛UnsupportedMediaTypeException异常解决的详细内容,更多关于WebClient抛UnsupportedMediaTypeException的资料请关注编程网其它相关文章!

--结束END--

本文标题: WebClient抛UnsupportedMediaTypeException异常解决

本文链接: https://lsjlt.com/news/140391.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • WebClient抛UnsupportedMediaTypeException异常解决
    目录前言问题背景问题分析解决方案方案一方案二方案三自定义解码器设置解码器方案四方案五方案六前言 前面分享了Spring5中的WebClient使用方法详解 后,就有朋友在s...
    99+
    2024-04-02
  • c#抛出ArgumentOutOfRangeException异常怎么解决
    在C#中抛出ArgumentOutOfRangeException异常通常是由于方法或函数的参数超出了有效范围。要解决这个问题,可以...
    99+
    2024-02-29
    ​C#
  • Python异步中loop抛出异常的解决方法
    这篇文章主要介绍Python异步中loop抛出异常的解决方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!python的五大特点是什么python的五大特点:1.简单易学,开发程序时,专注的是解决问题,而不是搞明白语...
    99+
    2023-06-14
  • python怎么抛出异常_python抛出异常教程
    1、首先基础异常处理try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。如果你不想在异常发生时结束你的程序,只需在try里捕获它。try的工作...
    99+
    2024-04-02
  • java抛出异常throw问题怎么解决
    在Java中,可以使用try-catch语句来处理抛出的异常。当抛出异常时,可以在try块中编写可能会引发异常的代码,并在catch...
    99+
    2023-09-12
    java
  • spring拦截器抛出异常怎么解决
    当Spring拦截器抛出异常时,可以根据需要采取以下几种解决方案:1. 异常处理器:使用Spring的异常处理器来处理拦截器抛出的异...
    99+
    2023-08-18
    spring
  • java mockito模拟抛出异常怎么解决
    在使用Mockito模拟方法抛出异常时,可以使用Mockito的doThrow()方法来模拟方法抛出异常。下面是一个简单的示例: 假...
    99+
    2024-03-13
    java
  • Spring应用抛出NoUniqueBeanDefinitionException异常的解决方案
    目录前言 解决方案 前言 我们在开发Spring应用时可能会不小心注入两个相同类型的Bean,比如实现了两个相同Service接口的类,示例伪代码如下: interface&n...
    99+
    2024-04-02
  • java异常处理throws完成异常抛出详解
    已检查异常抛出 对于已检查异常(checked exceptions),编译器强制要求捕获并处理可能发生的异常,不处理就不能通过编译。但调用的方法没有能力处理这种异常,对于这种情况,...
    99+
    2024-04-02
  • 分析MySQL抛出异常的几种常见解决方式
    目录前言一、代码配置的数据库名称或者密码与本地数据库不一致1.1、错误产生描述1.2、解决方式二、导入的非本地项目文件与本地的数据库版本不匹配2.1、错误产生描述2.2、解决方式三、MySQL 高版本配置加载驱动类包...
    99+
    2022-05-19
    mysql 异常
  • Java中使用throw-throws抛出异常如何解决
    这篇文章将为大家详细讲解有关Java中使用throw-throws抛出异常如何解决,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、throws抛出异常   &...
    99+
    2023-06-20
  • javax.servlet.ServletException: Servlet执行抛出一个异常怎么解决?
    javax.servlet.ServletException: Servlet执行抛出一个异常怎么解决? 站长亲测 数据库版本过高会导致报错 HTTP状态 500 - 内部服务器错误 如果未...
    99+
    2023-09-07
    servlet java mybatis
  • python3-抛出、捕获异常
    ''' 异常:错误发生的信号,程序随之终止 三个部分: 1.traceback 异常的追踪信息(链接) 2.异常的类型 3.异常信息 错误两大类: 1.语法错误(运行前进行判定和修正) 2.逻...
    99+
    2023-01-31
    抛出 异常
  • 浅谈python抛出异常、自定义异常, 传递异常
    一. 抛出异常 Python用异常对象(exception object)表示异常情况,遇到错误后,会引发异常。如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(Traceback,一种错误信息)终止执行...
    99+
    2022-06-04
    异常 自定义 浅谈
  • JS调用C++函数抛出异常及捕捉异常详解
    目录总结本文讲述如何利用v8::TryCatch捕捉js代码中发生的异常。 首先,声明TryCatch对象。 v8::TryCatch trycatch( isolate ); ...
    99+
    2024-04-02
  • 在springboot中springmvc出现抛出全局异常如何解决
    在springboot中springmvc出现抛出全局异常如何解决?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。springboot中抛出异常,springbo...
    99+
    2023-05-31
    springboot springmvc 全局异常
  • python raise语句重新抛出异常问题怎么解决
    这篇文章主要讲解了“python raise语句重新抛出异常问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“python raise语句重新抛出异常问题怎么解决”吧!说明raise...
    99+
    2023-06-30
  • 抛出异常时将异常信息返给前端
      全局异常处理器负责将抛出的异常,以统一的格式返给前端。在这里起主要作用的注解是@RestControllerAdvice。 @RestControllerAdvice主要配合@ExceptionHandler使用,统一处理异常情况。 1...
    99+
    2023-09-08
    前端 mybatis java
  • python自定义异常和主动抛出异常(r
    有时候python自带异常不够用,如同java,python也可以自定义异常,并且可以手动抛出。注意,自定义异常只能由自己抛出。python解释器是不知道用户自定义异常是什么鬼的。 主动抛出异常。 格式: 主动抛出异常终止...
    99+
    2023-01-31
    异常 自定义 抛出
  • C++ 函数异常处理中如何重抛异常?
    c++++ 中的异常重抛用于在捕获异常后重新抛出,以便程序的其他部分可以处理它。语法是:try { ... } catch (const std::exception& e) {...
    99+
    2024-04-15
    c++ 异常处理 重抛异常
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作