使用Spring WebClient读取OGC WFS的GML数据时遭遇InvalidMediaTypeException异常
使用Spring WebClient读取OGC WFS的GML数据时遭遇InvalidMediaTypeException异常
我在尝试用Spring WebClient从OGC WFS源读取GML数据时碰到了一个头疼的问题——服务要求的输出格式参数是OUTPUTFORMAT=text/xml;%20subtype=gml/3.1.1,但这会触发Spring抛出以下异常:
org.springframework.http.InvalidMediaTypeException: Invalid mime type "text/xml; subtype=gml/3.1.1;charset=UTF-8": Invalid token character '/' in token "gml/3.1.1"
我的调用代码是这样的:
private static final WebClient webClient = WebClient.create(); public static String fetchRemoteText(String url) { try { return webClient.get().uri(url).retrieve().bodyToMono(String.class).doOnSuccess(body -> { if (body == null || body.isBlank()) { log.info("Empty result returned."); } }).block(); } catch (Exception e) { log.error("Error fetching from URL {}: {}", url, e.getMessage(), e); return null; } }
试了ChatGPT给的各种方法都没解决,后来摸索出几个可行的方案,分享给你:
解决方案:绕过Spring的MediaType严格校验
问题根源在于Spring的MediaType类严格遵循RFC标准解析MIME类型,而WFS服务返回的subtype=gml/3.1.1里的斜杠不符合标准的token规则,所以我们需要跳过这个校验或者自定义解析逻辑。
方法1:自定义StringDecoder,直接读取响应体
我们可以给WebClient配置一个自定义的字符串解码器,不管响应的ContentType是什么,直接读取内容:
private static final WebClient webClient = WebClient.builder() .exchangeStrategies(ExchangeStrategies.builder() .codecs(configurer -> { // 按需调整内存大小,避免大内容溢出 configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024); configurer.defaultCodecs().stringDecoder(new StringDecoder() { @Override public Mono<String> decodeToMono(ClientHttpResponse response, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { // 跳过MediaType校验,直接读取响应字节转字符串 return response.getBody() .map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); return new String(bytes, StandardCharsets.UTF_8); }); } }); }) .build()) .build();
方法2:使用exchange()手动处理响应
放弃使用retrieve()的自动解析,改用exchangeToMono()手动处理响应,这样就能绕过Spring的MediaType校验:
public static String fetchRemoteText(String url) { try { return webClient.get().uri(url) .exchangeToMono(response -> { // 不管ContentType,直接把响应体转成字符串 return response.bodyToMono(String.class); }) .doOnSuccess(body -> { if (body == null || body.isBlank()) { log.info("Empty result returned."); } }) .block(); } catch (Exception e) { log.error("Error fetching from URL {}: {}", url, e.getMessage(), e); return null; } }
方法3:注册自定义MediaType(更规范的方式)
如果希望Spring能识别这种非标准的MIME类型,可以注册一个自定义的MediaType,或者让字符串解码器接受所有text/xml的变种:
@Configuration public class WebClientConfig { @Bean public WebClient webClient() { ExchangeStrategies strategies = ExchangeStrategies.builder() .codecs(configurer -> { // 让字符串解码器接受所有text/xml相关的类型,包括带自定义参数的 configurer.customCodecs().register(new StringDecoder(MediaType.TEXT_XML, MediaType.ALL)); }) .build(); return WebClient.builder() .exchangeStrategies(strategies) .build(); } }
总结
上面几个方法里,方法1和方法2比较直接,能快速解决问题;方法3更规范,但需要确保服务返回的类型在我们配置的范围内。你可以根据自己的项目情况选择合适的方案。
备注:内容来源于stack exchange,提问作者Loibologic
相关产品推荐
相关产品推荐

