Skip to content

SPR-17133: Fix content negotiation #1920

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions spring-core/src/main/java/org/springframework/util/MimeType.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
* <p>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.</p>
* @since 5.10.0
*/
private boolean parametersInclude(MimeType other) {
if (this.parameters.size() > other.parameters.size()) {
return false;
}

for (Map.Entry<String, String> 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.
Expand All @@ -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()) {
Expand All @@ -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.
* <p>Parameters are incompatible when they contain the same parameter
* with different values.</p>
* @since 5.10.0
*/
private boolean parametersAreCompatibleWith(MimeType other) {
for (Map.Entry<String, String> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -327,7 +327,7 @@ public final boolean match(List<MediaType> acceptedMediaTypes) {

private boolean matchMediaType(List<MediaType> acceptedMediaTypes) {
for (MediaType acceptedMediaType : acceptedMediaTypes) {
if (getMediaType().isCompatibleWith(acceptedMediaType)) {
+ if (acceptedMediaType.includes(getMediaType())) {
return true;
}
}
Expand Down