Versions

  • Spring Messaging 6.0.3 / 5.3.24

Problems

DefaultContentTypeResolver#resolve will return defaultMimeType if null is passed as MessageHeaders. https://github.com/spring-projects/spring-framework/blob/0fbc94fae002a2bf25b62b0591dcadffab817b94/spring-messaging/src/main/java/org/springframework/messaging/converter/DefaultContentTypeResolver.java#L58-L63

However, AbstractMessageConverter#getMimeType does not call the contentTypeResolver if null is passed as MessageHeaders. https://github.com/spring-projects/spring-framework/blob/0fbc94fae002a2bf25b62b0591dcadffab817b94/spring-messaging/src/main/java/org/springframework/messaging/converter/AbstractMessageConverter.java#L252-L255

Because of this, even if DefaultContentTypeResolver#defaultMimeType is set properly, it may be ignored.

I've confirmed this issue with Spring Cloud Stream CompositeMessageConverter. ApplicationJsonMessageMarshallingConverter (child converter of CompositeMessageConverter) has strictContentTypeMatch=true, so it thinks it's not a supported content type.

Fixes

ContentTypeResolver#resolve accepts null, so MessageConverter should always delegate resolution to it. Fix AbstractMessageConverter#getMimeType not checking the value of MessageHeaders.

Comment From: artembilan

Right, Rossen, it is not common to pass a null for header value. Even all our messaging API does not let do that: as long as we provide a null, that is a signal to remove that header. See a MessageHeaderAccessor for example:

    /**
     * Set the value for the given header name.
     * <p>If the provided value is {@code null}, the header will be removed.
     */
    public void setHeader(String name, @Nullable Object value) {
        if (isReadOnly(name)) {
            throw new IllegalArgumentException("'" + name + "' header is read-only");
        }
        verifyType(name, value);
        if (value != null) {
            // Modify header if necessary
            if (!ObjectUtils.nullSafeEquals(value, getHeader(name))) {
                this.modified = true;
                this.headers.getRawHeaders().put(name, value);
            }
        }
        else {
            // Remove header if available
            if (this.headers.containsKey(name)) {
                this.modified = true;
                this.headers.getRawHeaders().remove(name);
            }
        }
    }

Perhaps for consistency our MessageHeaders must be fixed respectively - to ignore entries of the input Map which has null values...