From 98ac665cae2eb992dc471a3529ce15674152df71 Mon Sep 17 00:00:00 2001 From: jam01 Date: Wed, 18 Jul 2018 20:40:21 -0500 Subject: [PATCH] Includes media type paremeters in content negotiation process --- .../org/springframework/util/MimeType.java | 57 ++++++++++++++++++- .../condition/ProducesRequestCondition.java | 4 +- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/MimeType.java b/spring-core/src/main/java/org/springframework/util/MimeType.java index 4fbc08eb4ae0..9a5a8e233416 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -316,7 +316,7 @@ public boolean includes(@Nullable MimeType other) { } else if (getType().equals(other.getType())) { if (getSubtype().equals(other.getSubtype())) { - return true; + return parametersInclude(other); } if (isWildcardSubtype()) { // Wildcard with suffix, e.g. application/*+xml @@ -340,6 +340,37 @@ else if (getType().equals(other.getType())) { } return false; } + + /** + * Determine if the parameters in this {@code MimeType} include those + * of the supplied {@code MimeType}, performing case-insensitive comparisons + * for {@link Charset}s. + *

Parameters are not included when this contains more parameters than + * the supplied, when this contains a parameter that the supplied does not, + * or when they both contain the same parameter with different values.

+ * @since 5.10.0 + */ + private boolean parametersInclude(MimeType other) { + if (this.parameters.size() > other.parameters.size()) { + return false; + } + + for (Map.Entry entry : this.parameters.entrySet()) { + String key = entry.getKey(); + if (!other.parameters.containsKey(key)) { + return false; + } + if (PARAM_CHARSET.equals(key)) { + if (getCharset() != null && !getCharset().equals(other.getCharset())) + return false; + } + else if (!ObjectUtils.nullSafeEquals(entry.getValue(), other.parameters.get(key))) { + return false; + } + } + + return true; + } /** * Indicate whether this MIME Type is compatible with the given MIME Type. @@ -359,7 +390,7 @@ public boolean isCompatibleWith(@Nullable MimeType other) { } else if (getType().equals(other.getType())) { if (getSubtype().equals(other.getSubtype())) { - return true; + return parametersAreCompatibleWith(other); } // Wildcard with suffix? e.g. application/*+xml if (isWildcardSubtype() || other.isWildcardSubtype()) { @@ -382,6 +413,28 @@ else if (thisPlusIdx != -1 && otherPlusIdx != -1) { } return false; } + + /** + * Determine if the parameters in this {@code MimeType} and the supplied + * {@code MimeType} are compatible, performing case-insensitive comparisons + * for {@link Charset}s. + *

Parameters are incompatible when they contain the same parameter + * with different values.

+ * @since 5.10.0 + */ + private boolean parametersAreCompatibleWith(MimeType other) { + for (Map.Entry entry : this.parameters.entrySet()) { + String key = entry.getKey(); + if (PARAM_CHARSET.equals(key)) { + if (other.getCharset() != null && !other.getCharset().equals(getCharset())) + return false; + } + else if (other.parameters.containsKey(key) && !entry.getValue().equals(other.parameters.get(key))) + return false; + } + + return true; + } @Override diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java index a3c816adb54b..fae4b345eaa7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java @@ -178,7 +178,7 @@ public ProducesRequestCondition combine(ProducesRequestCondition other) { * Checks if any of the contained media type expressions match the given * request 'Content-Type' header and returns an instance that is guaranteed * to contain matching expressions only. The match is performed via - * {@link MediaType#isCompatibleWith(MediaType)}. + * {@link MediaType#includes(MediaType)}. * @param request the current request * @return the same instance if there are no expressions; * or a new condition with matching expressions; @@ -327,7 +327,7 @@ public final boolean match(List acceptedMediaTypes) { private boolean matchMediaType(List acceptedMediaTypes) { for (MediaType acceptedMediaType : acceptedMediaTypes) { - if (getMediaType().isCompatibleWith(acceptedMediaType)) { ++ if (acceptedMediaType.includes(getMediaType())) { return true; } }