Skip to content

Commit ed7684d

Browse files
committed
Don't implicitly check preconditions on PUT requests
Prior to this commit, the `HttpEntityMethodProcessor` would check HTTP conditions on non-safe requests (i.e. not GET/HEAD). This would prevent Controllers from returning `ResponseEntity` containing response headers with updated values of `"Last-Modified"` or `ETag` once the resource has been updated. This commit avoids those checks for non GET/HEAD requests - this code can still be leveraged from Controllers themselves using `ServletWebRequest::checkNotModified` methods. Issue: SPR-15780
1 parent 50253f6 commit ed7684d

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,8 +21,11 @@
2121
import java.lang.reflect.Type;
2222
import java.util.ArrayList;
2323
import java.util.Collections;
24+
import java.util.EnumSet;
2425
import java.util.List;
2526
import java.util.Map;
27+
import java.util.Set;
28+
2629
import javax.servlet.http.HttpServletRequest;
2730
import javax.servlet.http.HttpServletResponse;
2831

@@ -66,6 +69,8 @@
6669
*/
6770
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
6871

72+
private static final Set<HttpMethod> SAFE_METHODS = EnumSet.of(HttpMethod.GET, HttpMethod.HEAD);
73+
6974
/**
7075
* Basic constructor with converters only. Suitable for resolving
7176
* {@code HttpEntity}. For handling {@code ResponseEntity} consider also
@@ -199,7 +204,8 @@ public void handleReturnValue(@Nullable Object returnValue, MethodParameter retu
199204
int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue();
200205
outputMessage.getServletResponse().setStatus(returnStatus);
201206
if (returnStatus == 200) {
202-
if (isResourceNotModified(inputMessage, outputMessage)) {
207+
if (SAFE_METHODS.contains(inputMessage.getMethod())
208+
&& isResourceNotModified(inputMessage, outputMessage)) {
203209
// Ensure headers are flushed, no body should be written.
204210
outputMessage.flush();
205211
// Skip call to converters, as they may update the body.

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import java.nio.charset.StandardCharsets;
2323
import java.time.ZoneId;
2424
import java.time.ZonedDateTime;
25+
import java.time.temporal.ChronoUnit;
2526
import java.util.Arrays;
2627
import java.util.Collections;
2728
import java.util.Date;
@@ -558,6 +559,21 @@ public void shouldHandleValidatorHeadersInPutResponses() throws Exception {
558559
assertConditionalResponse(HttpStatus.OK, "body", etagValue, -1);
559560
}
560561

562+
@Test
563+
public void shouldNotFailPreconditionForPutRequests() throws Exception {
564+
servletRequest.setMethod("PUT");
565+
ZonedDateTime dateTime = ofEpochMilli(new Date().getTime()).atZone(GMT);
566+
servletRequest.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, RFC_1123_DATE_TIME.format(dateTime));
567+
568+
long justModified = dateTime.plus(1, ChronoUnit.SECONDS).toEpochSecond() * 1000;
569+
ResponseEntity<String> returnValue = ResponseEntity.ok()
570+
.lastModified(justModified).body("body");
571+
initStringMessageConversion(TEXT_PLAIN);
572+
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
573+
574+
assertConditionalResponse(HttpStatus.OK, null, null, justModified);
575+
}
576+
561577
@Test
562578
public void varyHeader() throws Exception {
563579
String[] entityValues = {"Accept-Language", "User-Agent"};

0 commit comments

Comments
 (0)