mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
String decoding for text only vs any MIME type
Follow-up to: https://github.com/spring-projects/spring-framework/commit/3d68c496f15c80a75711480a569cda1f7c64dc91 StringDecoder can be created in text-only vs "*/*" mode which in turn allows a more intuitive order of client side decoders, e.g. SSE does not have to be ahead of StringDecoder. The commit also explicitly disables String from the supported types in Jackson2Decoder leaving it to the StringDecoder in "*/*" mode which comes after. This does not change the current arrangement since the the StringDecoder ahead having "*/*" picks up JSON content just the same. From a broader perspective this change allows any decoder to deal with String if it wants to after examining the content type be it the SSE or another, custom decoder. For Jackson there is very little value in decoding to String which works only if the output contains a single JSON string but will fail to parse anything else (JSON object/array) while StringDecoder in "*/*" mode will not fail. Issue: SPR-15374
This commit is contained in:
+4
-1
@@ -55,7 +55,7 @@ public class ServerSentEventHttpMessageReader implements HttpMessageReader<Objec
|
||||
|
||||
private static final DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
|
||||
|
||||
private static final StringDecoder stringDecoder = new StringDecoder(false);
|
||||
private static final StringDecoder stringDecoder = StringDecoder.textPlainOnly(false);
|
||||
|
||||
|
||||
private final Decoder<?> decoder;
|
||||
@@ -190,6 +190,9 @@ public class ServerSentEventHttpMessageReader implements HttpMessageReader<Objec
|
||||
public Mono<Object> readMono(ResolvableType elementType, ReactiveHttpInputMessage message,
|
||||
Map<String, Object> hints) {
|
||||
|
||||
// We're ahead of String + "*/*"
|
||||
// Let's see if we can aggregate the output (lest we time out)...
|
||||
|
||||
if (String.class.equals(elementType.getRawClass())) {
|
||||
Flux<DataBuffer> body = message.getBody();
|
||||
return stringDecoder.decodeToMono(body, elementType, null, null).cast(Object.class);
|
||||
|
||||
@@ -74,6 +74,11 @@ public abstract class Jackson2CodecSupport {
|
||||
}
|
||||
|
||||
|
||||
protected boolean supportsMimeType(MimeType mimeType) {
|
||||
return mimeType == null ||
|
||||
JSON_MIME_TYPES.stream().anyMatch(m -> m.isCompatibleWith(mimeType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Jackson {@link JavaType} for the specified type and context class.
|
||||
* <p>The default implementation returns {@code typeFactory.constructType(type, contextClass)},
|
||||
|
||||
+4
-2
@@ -67,10 +67,12 @@ public class Jackson2JsonDecoder extends Jackson2CodecSupport implements HttpDec
|
||||
@Override
|
||||
public boolean canDecode(ResolvableType elementType, MimeType mimeType) {
|
||||
JavaType javaType = this.mapper.getTypeFactory().constructType(elementType.getType());
|
||||
return this.mapper.canDeserialize(javaType) &&
|
||||
(mimeType == null || JSON_MIME_TYPES.stream().anyMatch(m -> m.isCompatibleWith(mimeType)));
|
||||
// Skip String (CharSequenceDecoder + "*/*" comes after)
|
||||
return !CharSequence.class.isAssignableFrom(elementType.resolve(Object.class)) &&
|
||||
this.mapper.canDeserialize(javaType) && supportsMimeType(mimeType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<MimeType> getDecodableMimeTypes() {
|
||||
return JSON_MIME_TYPES;
|
||||
|
||||
+2
-2
@@ -104,8 +104,8 @@ public class Jackson2JsonEncoder extends Jackson2CodecSupport implements HttpEnc
|
||||
|
||||
@Override
|
||||
public boolean canEncode(ResolvableType elementType, MimeType mimeType) {
|
||||
return this.mapper.canSerialize(elementType.getRawClass()) &&
|
||||
(mimeType == null || JSON_MIME_TYPES.stream().anyMatch(m -> m.isCompatibleWith(mimeType)));
|
||||
Class<?> clazz = elementType.getRawClass();
|
||||
return this.mapper.canSerialize(clazz) && supportsMimeType(mimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user