spring HttpEntityMethodProcessorMockTests 源码
spring HttpEntityMethodProcessorMockTests 代码
文件路径:/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Set;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ProblemDetail;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.ErrorResponse;
import org.springframework.web.ErrorResponseException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import static java.time.Instant.ofEpochMilli;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
import static org.springframework.http.MediaType.APPLICATION_PROBLEM_JSON;
import static org.springframework.http.MediaType.APPLICATION_PROBLEM_JSON_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN;
import static org.springframework.web.servlet.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
/**
* Test fixture for {@link HttpEntityMethodProcessor} delegating to a mock
* {@link HttpMessageConverter}.
*
* <p>Also see {@link HttpEntityMethodProcessorTests}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Brian Clozel
*/
public class HttpEntityMethodProcessorMockTests {
private static final ZoneId GMT = ZoneId.of("GMT");
private HttpEntityMethodProcessor processor;
private HttpMessageConverter<String> stringHttpMessageConverter;
private HttpMessageConverter<Resource> resourceMessageConverter;
private HttpMessageConverter<Object> resourceRegionMessageConverter;
private HttpMessageConverter<Object> jsonMessageConverter;
private MethodParameter paramHttpEntity;
private MethodParameter paramRequestEntity;
private MethodParameter paramResponseEntity;
private MethodParameter paramInt;
private MethodParameter returnTypeResponseEntity;
private MethodParameter returnTypeResponseEntityProduces;
private MethodParameter returnTypeResponseEntityResource;
private MethodParameter returnTypeHttpEntity;
private MethodParameter returnTypeHttpEntitySubclass;
private MethodParameter returnTypeInt;
private MethodParameter returnTypeErrorResponse;
private MethodParameter returnTypeProblemDetail;
private ModelAndViewContainer mavContainer;
private MockHttpServletRequest servletRequest;
private MockHttpServletResponse servletResponse;
private ServletWebRequest webRequest;
@BeforeEach
@SuppressWarnings("unchecked")
public void setup() throws Exception {
stringHttpMessageConverter = mock(HttpMessageConverter.class);
given(stringHttpMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(TEXT_PLAIN));
resourceMessageConverter = mock(HttpMessageConverter.class);
given(resourceMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.ALL));
given(resourceMessageConverter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(MediaType.ALL));
resourceRegionMessageConverter = mock(HttpMessageConverter.class);
given(resourceRegionMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.ALL));
given(resourceRegionMessageConverter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(MediaType.ALL));
jsonMessageConverter = mock(HttpMessageConverter.class);
given(jsonMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.APPLICATION_PROBLEM_JSON));
given(jsonMessageConverter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(MediaType.APPLICATION_PROBLEM_JSON));
processor = new HttpEntityMethodProcessor(Arrays.asList(
stringHttpMessageConverter, resourceMessageConverter, resourceRegionMessageConverter, jsonMessageConverter));
Method handle1 = getClass().getMethod("handle1", HttpEntity.class, ResponseEntity.class,
Integer.TYPE, RequestEntity.class);
paramHttpEntity = new MethodParameter(handle1, 0);
paramRequestEntity = new MethodParameter(handle1, 3);
paramResponseEntity = new MethodParameter(handle1, 1);
paramInt = new MethodParameter(handle1, 2);
returnTypeResponseEntity = new MethodParameter(handle1, -1);
returnTypeResponseEntityProduces = new MethodParameter(getClass().getMethod("handle4"), -1);
returnTypeHttpEntity = new MethodParameter(getClass().getMethod("handle2", HttpEntity.class), -1);
returnTypeHttpEntitySubclass = new MethodParameter(getClass().getMethod("handle2x", HttpEntity.class), -1);
returnTypeInt = new MethodParameter(getClass().getMethod("handle3"), -1);
returnTypeResponseEntityResource = new MethodParameter(getClass().getMethod("handle5"), -1);
returnTypeErrorResponse = new MethodParameter(getClass().getMethod("handle6"), -1);
returnTypeProblemDetail = new MethodParameter(getClass().getMethod("handle7"), -1);
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest("GET", "/foo");
servletResponse = new MockHttpServletResponse();
webRequest = new ServletWebRequest(servletRequest, servletResponse);
}
@Test
public void supportsParameter() {
assertThat(processor.supportsParameter(paramHttpEntity)).as("HttpEntity parameter not supported").isTrue();
assertThat(processor.supportsParameter(paramRequestEntity)).as("RequestEntity parameter not supported").isTrue();
assertThat(processor.supportsParameter(paramResponseEntity)).as("ResponseEntity parameter supported").isFalse();
assertThat(processor.supportsParameter(paramInt)).as("non-entity parameter supported").isFalse();
}
@Test
public void supportsReturnType() {
assertThat(processor.supportsReturnType(returnTypeResponseEntity)).as("ResponseEntity return type not supported").isTrue();
assertThat(processor.supportsReturnType(returnTypeHttpEntity)).as("HttpEntity return type not supported").isTrue();
assertThat(processor.supportsReturnType(returnTypeHttpEntitySubclass)).as("Custom HttpEntity subclass not supported").isTrue();
assertThat(processor.supportsReturnType(returnTypeErrorResponse)).isTrue();
assertThat(processor.supportsReturnType(returnTypeProblemDetail)).isTrue();
assertThat(processor.supportsReturnType(paramRequestEntity)).as("RequestEntity parameter supported").isFalse();
assertThat(processor.supportsReturnType(returnTypeInt)).as("non-ResponseBody return type supported").isFalse();
}
@Test
public void shouldResolveHttpEntityArgument() throws Exception {
String body = "Foo";
MediaType contentType = TEXT_PLAIN;
servletRequest.addHeader("Content-Type", contentType.toString());
servletRequest.setContent(body.getBytes(StandardCharsets.UTF_8));
given(stringHttpMessageConverter.canRead(String.class, contentType)).willReturn(true);
given(stringHttpMessageConverter.read(eq(String.class), isA(HttpInputMessage.class))).willReturn(body);
Object result = processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null);
assertThat(result instanceof HttpEntity).isTrue();
assertThat(mavContainer.isRequestHandled()).as("The requestHandled flag shouldn't change").isFalse();
assertThat(((HttpEntity<?>) result).getBody()).as("Invalid argument").isEqualTo(body);
}
@Test
public void shouldResolveRequestEntityArgument() throws Exception {
String body = "Foo";
MediaType contentType = TEXT_PLAIN;
servletRequest.addHeader("Content-Type", contentType.toString());
servletRequest.setMethod("GET");
servletRequest.setServerName("www.example.com");
servletRequest.setServerPort(80);
servletRequest.setRequestURI("/path");
servletRequest.setContent(body.getBytes(StandardCharsets.UTF_8));
given(stringHttpMessageConverter.canRead(String.class, contentType)).willReturn(true);
given(stringHttpMessageConverter.read(eq(String.class), isA(HttpInputMessage.class))).willReturn(body);
Object result = processor.resolveArgument(paramRequestEntity, mavContainer, webRequest, null);
assertThat(result instanceof RequestEntity).isTrue();
assertThat(mavContainer.isRequestHandled()).as("The requestHandled flag shouldn't change").isFalse();
RequestEntity<?> requestEntity = (RequestEntity<?>) result;
assertThat(requestEntity.getMethod()).as("Invalid method").isEqualTo(HttpMethod.GET);
// using default port (which is 80), so do not need to append the port (-1 means ignore)
URI uri = new URI("http", null, "www.example.com", -1, "/path", null, null);
assertThat(requestEntity.getUrl()).as("Invalid url").isEqualTo(uri);
assertThat(requestEntity.getBody()).as("Invalid argument").isEqualTo(body);
}
@Test
public void shouldFailResolvingWhenConverterCannotRead() throws Exception {
MediaType contentType = TEXT_PLAIN;
servletRequest.setMethod("POST");
servletRequest.addHeader("Content-Type", contentType.toString());
given(stringHttpMessageConverter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(contentType));
given(stringHttpMessageConverter.canRead(String.class, contentType)).willReturn(false);
assertThatExceptionOfType(HttpMediaTypeNotSupportedException.class).isThrownBy(() ->
processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null));
}
@Test
public void shouldFailResolvingWhenContentTypeNotSupported() throws Exception {
servletRequest.setMethod("POST");
servletRequest.setContent("some content".getBytes(StandardCharsets.UTF_8));
assertThatExceptionOfType(HttpMediaTypeNotSupportedException.class).isThrownBy(() ->
processor.resolveArgument(paramHttpEntity, mavContainer, webRequest, null));
}
@Test
public void shouldHandleReturnValue() throws Exception {
String body = "Foo";
ResponseEntity<String> returnValue = new ResponseEntity<>(body, HttpStatus.OK);
MediaType accepted = TEXT_PLAIN;
servletRequest.addHeader("Accept", accepted.toString());
initStringMessageConversion(accepted);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertThat(mavContainer.isRequestHandled()).isTrue();
verify(stringHttpMessageConverter).write(eq(body), eq(accepted), isA(HttpOutputMessage.class));
}
@Test
public void shouldHandleErrorResponse() throws Exception {
ErrorResponseException ex = new ErrorResponseException(HttpStatus.BAD_REQUEST);
ex.getHeaders().add("foo", "bar");
servletRequest.addHeader("Accept", APPLICATION_PROBLEM_JSON_VALUE);
given(jsonMessageConverter.canWrite(ProblemDetail.class, APPLICATION_PROBLEM_JSON)).willReturn(true);
processor.handleReturnValue(ex, returnTypeProblemDetail, mavContainer, webRequest);
assertThat(mavContainer.isRequestHandled()).isTrue();
assertThat(webRequest.getNativeResponse(HttpServletResponse.class).getStatus()).isEqualTo(400);
verify(jsonMessageConverter).write(eq(ex.getBody()), eq(APPLICATION_PROBLEM_JSON), isA(HttpOutputMessage.class));
assertThat(ex.getBody()).isNotNull()
.extracting(ProblemDetail::getInstance).isNotNull()
.extracting(URI::toString)
.as("Instance was not set to the request path")
.isEqualTo(servletRequest.getRequestURI());
// But if instance is set, it should be respected
ex.getBody().setInstance(URI.create("/something/else"));
processor.handleReturnValue(ex, returnTypeProblemDetail, mavContainer, webRequest);
assertThat(ex.getBody()).isNotNull()
.extracting(ProblemDetail::getInstance).isNotNull()
.extracting(URI::toString)
.as("Instance was not set to the request path")
.isEqualTo("/something/else");
}
@Test
public void shouldHandleProblemDetail() throws Exception {
ProblemDetail problemDetail = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
servletRequest.addHeader("Accept", APPLICATION_PROBLEM_JSON_VALUE);
given(jsonMessageConverter.canWrite(ProblemDetail.class, APPLICATION_PROBLEM_JSON)).willReturn(true);
processor.handleReturnValue(problemDetail, returnTypeProblemDetail, mavContainer, webRequest);
assertThat(mavContainer.isRequestHandled()).isTrue();
assertThat(webRequest.getNativeResponse(HttpServletResponse.class).getStatus()).isEqualTo(400);
verify(jsonMessageConverter).write(eq(problemDetail), eq(APPLICATION_PROBLEM_JSON), isA(HttpOutputMessage.class));
assertThat(problemDetail)
.extracting(ProblemDetail::getInstance).isNotNull()
.extracting(URI::toString)
.as("Instance was not set to the request path")
.isEqualTo(servletRequest.getRequestURI());
// But if instance is set, it should be respected
problemDetail.setInstance(URI.create("/something/else"));
processor.handleReturnValue(problemDetail, returnTypeProblemDetail, mavContainer, webRequest);
assertThat(problemDetail).isNotNull()
.extracting(ProblemDetail::getInstance).isNotNull()
.extracting(URI::toString)
.as("Instance was not set to the request path")
.isEqualTo("/something/else");
}
@Test
public void shouldHandleReturnValueWithProducibleMediaType() throws Exception {
String body = "Foo";
ResponseEntity<String> returnValue = new ResponseEntity<>(body, HttpStatus.OK);
servletRequest.addHeader("Accept", "text/*");
servletRequest.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
given(stringHttpMessageConverter.canWrite(String.class, MediaType.TEXT_HTML)).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest);
assertThat(mavContainer.isRequestHandled()).isTrue();
verify(stringHttpMessageConverter).write(eq(body), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
}
@Test
@SuppressWarnings("unchecked")
public void shouldHandleReturnValueWithResponseBodyAdvice() throws Exception {
servletRequest.addHeader("Accept", "text/*");
servletRequest.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, Collections.singleton(MediaType.TEXT_HTML));
ResponseEntity<String> returnValue = new ResponseEntity<>(HttpStatus.OK);
ResponseBodyAdvice<String> advice = mock(ResponseBodyAdvice.class);
given(advice.supports(any(), any())).willReturn(true);
given(advice.beforeBodyWrite(any(), any(), any(), any(), any(), any())).willReturn("Foo");
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(
Collections.singletonList(stringHttpMessageConverter), null, Collections.singletonList(advice));
reset(stringHttpMessageConverter);
given(stringHttpMessageConverter.canWrite(String.class, MediaType.TEXT_HTML)).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertThat(mavContainer.isRequestHandled()).isTrue();
verify(stringHttpMessageConverter).write(eq("Foo"), eq(MediaType.TEXT_HTML), isA(HttpOutputMessage.class));
}
@Test
public void shouldFailHandlingWhenContentTypeNotSupported() throws Exception {
String body = "Foo";
ResponseEntity<String> returnValue = new ResponseEntity<>(body, HttpStatus.OK);
MediaType accepted = MediaType.APPLICATION_ATOM_XML;
servletRequest.addHeader("Accept", accepted.toString());
given(stringHttpMessageConverter.canWrite(String.class, null)).willReturn(true);
given(stringHttpMessageConverter.getSupportedMediaTypes(any()))
.willReturn(Collections.singletonList(TEXT_PLAIN));
assertThatExceptionOfType(HttpMediaTypeNotAcceptableException.class).isThrownBy(() ->
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest));
}
@Test // gh-23205
public void shouldFailWithServerErrorIfContentTypeFromResponseEntity() {
ResponseEntity<String> returnValue = ResponseEntity.ok()
.contentType(MediaType.APPLICATION_XML)
.body("<foo/>");
given(stringHttpMessageConverter.canWrite(String.class, TEXT_PLAIN)).willReturn(true);
given(stringHttpMessageConverter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(TEXT_PLAIN));
assertThatThrownBy(() ->
processor.handleReturnValue(
returnValue, returnTypeResponseEntity, mavContainer, webRequest))
.isInstanceOf(HttpMessageNotWritableException.class)
.hasMessageContaining("with preset Content-Type");
}
@Test // gh-23287
public void shouldFailWithServerErrorIfContentTypeFromProducibleAttribute() {
Set<MediaType> mediaTypes = Collections.singleton(MediaType.APPLICATION_XML);
servletRequest.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
ResponseEntity<String> returnValue = ResponseEntity.ok().body("<foo/>");
given(stringHttpMessageConverter.canWrite(String.class, TEXT_PLAIN)).willReturn(true);
given(stringHttpMessageConverter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(TEXT_PLAIN));
assertThatThrownBy(() ->
processor.handleReturnValue(
returnValue, returnTypeResponseEntity, mavContainer, webRequest))
.isInstanceOf(HttpMessageNotWritableException.class)
.hasMessageContaining("with preset Content-Type");
}
@Test
public void shouldFailHandlingWhenConverterCannotWrite() throws Exception {
String body = "Foo";
ResponseEntity<String> returnValue = new ResponseEntity<>(body, HttpStatus.OK);
MediaType accepted = TEXT_PLAIN;
servletRequest.addHeader("Accept", accepted.toString());
given(stringHttpMessageConverter.canWrite(String.class, null)).willReturn(true);
given(stringHttpMessageConverter.getSupportedMediaTypes(any()))
.willReturn(Collections.singletonList(TEXT_PLAIN));
given(stringHttpMessageConverter.canWrite(String.class, accepted)).willReturn(false);
assertThatExceptionOfType(HttpMediaTypeNotAcceptableException.class).isThrownBy(() ->
processor.handleReturnValue(returnValue, returnTypeResponseEntityProduces, mavContainer, webRequest));
}
@Test // SPR-9142
public void shouldFailHandlingWhenAcceptHeaderIllegal() throws Exception {
ResponseEntity<String> returnValue = new ResponseEntity<>("Body", HttpStatus.ACCEPTED);
servletRequest.addHeader("Accept", "01");
assertThatExceptionOfType(HttpMediaTypeNotAcceptableException.class).isThrownBy(() ->
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest));
}
@Test
public void shouldHandleResponseHeaderNoBody() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.set("headerName", "headerValue");
ResponseEntity<String> returnValue = new ResponseEntity<>(headers, HttpStatus.ACCEPTED);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertThat(mavContainer.isRequestHandled()).isTrue();
assertThat(servletResponse.getHeader("headerName")).isEqualTo("headerValue");
}
@Test
public void shouldHandleResponseHeaderAndBody() throws Exception {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("header", "headerValue");
ResponseEntity<String> returnValue = new ResponseEntity<>("body", responseHeaders, HttpStatus.ACCEPTED);
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
ArgumentCaptor<HttpOutputMessage> outputMessage = ArgumentCaptor.forClass(HttpOutputMessage.class);
verify(stringHttpMessageConverter).write(eq("body"), eq(TEXT_PLAIN), outputMessage.capture());
assertThat(mavContainer.isRequestHandled()).isTrue();
assertThat(outputMessage.getValue().getHeaders().get("header").get(0)).isEqualTo("headerValue");
}
@Test
public void shouldHandleLastModifiedWithHttp304() throws Exception {
long currentTime = new Date().getTime();
long oneMinuteAgo = currentTime - (1000 * 60);
ZonedDateTime dateTime = ofEpochMilli(currentTime).atZone(GMT);
servletRequest.addHeader(HttpHeaders.IF_MODIFIED_SINCE, RFC_1123_DATE_TIME.format(dateTime));
ResponseEntity<String> returnValue = ResponseEntity.ok().lastModified(oneMinuteAgo).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.NOT_MODIFIED, null, null, oneMinuteAgo);
}
@Test
public void handleEtagWithHttp304() throws Exception {
String etagValue = "\"deadb33f8badf00d\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, etagValue);
ResponseEntity<String> returnValue = ResponseEntity.ok().eTag(etagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.NOT_MODIFIED, null, etagValue, -1);
}
@Test
public void handleEtagWithHttp304AndEtagFilterHasNoImpact() throws Exception {
String eTagValue = "\"deadb33f8badf00d\"";
FilterChain chain = (req, res) -> {
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, eTagValue);
ResponseEntity<String> returnValue = ResponseEntity.ok().eTag(eTagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
try {
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
};
new ShallowEtagHeaderFilter().doFilter(this.servletRequest, this.servletResponse, chain);
assertConditionalResponse(HttpStatus.NOT_MODIFIED, null, eTagValue, -1);
}
@Test // SPR-14559
public void shouldHandleInvalidIfNoneMatchWithHttp200() throws Exception {
String etagValue = "\"deadb33f8badf00d\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, "unquoted");
ResponseEntity<String> returnValue = ResponseEntity.ok().eTag(etagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.OK, "body", etagValue, -1);
}
@Test
public void shouldHandleETagAndLastModifiedWithHttp304() throws Exception {
long currentTime = new Date().getTime();
long oneMinuteAgo = currentTime - (1000 * 60);
String etagValue = "\"deadb33f8badf00d\"";
ZonedDateTime dateTime = ofEpochMilli(currentTime).atZone(GMT);
servletRequest.addHeader(HttpHeaders.IF_MODIFIED_SINCE, RFC_1123_DATE_TIME.format(dateTime));
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, etagValue);
ResponseEntity<String> returnValue = ResponseEntity.ok()
.eTag(etagValue).lastModified(oneMinuteAgo).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.NOT_MODIFIED, null, etagValue, oneMinuteAgo);
}
@Test
public void shouldHandleNotModifiedResponse() throws Exception {
long currentTime = new Date().getTime();
long oneMinuteAgo = currentTime - (1000 * 60);
String etagValue = "\"deadb33f8badf00d\"";
ResponseEntity<String> returnValue = ResponseEntity.status(HttpStatus.NOT_MODIFIED)
.eTag(etagValue).lastModified(oneMinuteAgo).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.NOT_MODIFIED, null, etagValue, oneMinuteAgo);
}
@Test
public void shouldHandleChangedETagAndLastModified() throws Exception {
long currentTime = new Date().getTime();
long oneMinuteAgo = currentTime - (1000 * 60);
String etagValue = "\"deadb33f8badf00d\"";
String changedEtagValue = "\"changed-etag-value\"";
ZonedDateTime dateTime = ofEpochMilli(currentTime).atZone(GMT);
servletRequest.addHeader(HttpHeaders.IF_MODIFIED_SINCE, RFC_1123_DATE_TIME.format(dateTime));
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, etagValue);
ResponseEntity<String> returnValue = ResponseEntity.ok()
.eTag(changedEtagValue).lastModified(oneMinuteAgo).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.OK, null, changedEtagValue, oneMinuteAgo);
}
@Test // SPR-13496
public void shouldHandleConditionalRequestIfNoneMatchWildcard() throws Exception {
String wildcardValue = "*";
String etagValue = "\"some-etag\"";
servletRequest.setMethod("POST");
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, wildcardValue);
ResponseEntity<String> returnValue = ResponseEntity.ok().eTag(etagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.OK, "body", etagValue, -1);
}
@Test // SPR-13626
public void shouldHandleGetIfNoneMatchWildcard() throws Exception {
String wildcardValue = "*";
String etagValue = "\"some-etag\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, wildcardValue);
ResponseEntity<String> returnValue = ResponseEntity.ok().eTag(etagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.OK, "body", etagValue, -1);
}
@Test // SPR-13626
public void shouldHandleIfNoneMatchIfMatch() throws Exception {
String etagValue = "\"some-etag\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, etagValue);
servletRequest.addHeader(HttpHeaders.IF_MATCH, "ifmatch");
ResponseEntity<String> returnValue = ResponseEntity.ok().eTag(etagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.NOT_MODIFIED, null, etagValue, -1);
}
@Test // SPR-13626
public void shouldHandleIfNoneMatchIfUnmodifiedSince() throws Exception {
String etagValue = "\"some-etag\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, etagValue);
ZonedDateTime dateTime = ofEpochMilli(new Date().getTime()).atZone(GMT);
servletRequest.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, RFC_1123_DATE_TIME.format(dateTime));
ResponseEntity<String> returnValue = ResponseEntity.ok().eTag(etagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.NOT_MODIFIED, null, etagValue, -1);
}
@Test
public void shouldHandleResource() throws Exception {
ResponseEntity<Resource> returnValue = ResponseEntity
.ok(new ByteArrayResource("Content".getBytes(StandardCharsets.UTF_8)));
given(resourceMessageConverter.canWrite(ByteArrayResource.class, null)).willReturn(true);
given(resourceMessageConverter.getSupportedMediaTypes(any())).willReturn(Collections.singletonList(MediaType.ALL));
given(resourceMessageConverter.canWrite(ByteArrayResource.class, APPLICATION_OCTET_STREAM)).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest);
then(resourceMessageConverter).should(times(1)).write(
any(ByteArrayResource.class), eq(APPLICATION_OCTET_STREAM), any(HttpOutputMessage.class));
assertThat(servletResponse.getStatus()).isEqualTo(200);
}
@Test
public void shouldHandleResourceByteRange() throws Exception {
ResponseEntity<Resource> returnValue = ResponseEntity
.ok(new ByteArrayResource("Content".getBytes(StandardCharsets.UTF_8)));
servletRequest.addHeader("Range", "bytes=0-5");
given(resourceRegionMessageConverter.canWrite(any(), eq(null))).willReturn(true);
given(resourceRegionMessageConverter.canWrite(any(), eq(APPLICATION_OCTET_STREAM))).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest);
then(resourceRegionMessageConverter).should(times(1)).write(
anyCollection(), eq(APPLICATION_OCTET_STREAM),
argThat(outputMessage -> "bytes".equals(outputMessage.getHeaders().getFirst(HttpHeaders.ACCEPT_RANGES))));
assertThat(servletResponse.getStatus()).isEqualTo(206);
}
@Test
public void handleReturnTypeResourceIllegalByteRange() throws Exception {
ResponseEntity<Resource> returnValue = ResponseEntity
.ok(new ByteArrayResource("Content".getBytes(StandardCharsets.UTF_8)));
servletRequest.addHeader("Range", "illegal");
given(resourceRegionMessageConverter.canWrite(any(), eq(null))).willReturn(true);
given(resourceRegionMessageConverter.canWrite(any(), eq(APPLICATION_OCTET_STREAM))).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest);
then(resourceRegionMessageConverter).should(never()).write(
anyCollection(), eq(APPLICATION_OCTET_STREAM), any(HttpOutputMessage.class));
assertThat(servletResponse.getStatus()).isEqualTo(416);
}
@Test //SPR-16754
public void disableRangeSupportForStreamingResponses() throws Exception {
InputStream is = new ByteArrayInputStream("Content".getBytes(StandardCharsets.UTF_8));
InputStreamResource resource = new InputStreamResource(is, "test");
ResponseEntity<Resource> returnValue = ResponseEntity.ok(resource);
servletRequest.addHeader("Range", "bytes=0-5");
given(resourceMessageConverter.canWrite(any(), eq(null))).willReturn(true);
given(resourceMessageConverter.canWrite(any(), eq(APPLICATION_OCTET_STREAM))).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest);
then(resourceMessageConverter).should(times(1)).write(
any(InputStreamResource.class), eq(APPLICATION_OCTET_STREAM), any(HttpOutputMessage.class));
assertThat(servletResponse.getStatus()).isEqualTo(200);
assertThat(servletResponse.getHeader(HttpHeaders.ACCEPT_RANGES)).isNull();
}
@Test //SPR-16921
public void disableRangeSupportIfContentRangePresent() throws Exception {
ResponseEntity<Resource> returnValue = ResponseEntity
.status(HttpStatus.PARTIAL_CONTENT)
.header(HttpHeaders.RANGE, "bytes=0-5")
.body(new ByteArrayResource("Content".getBytes(StandardCharsets.UTF_8)));
given(resourceRegionMessageConverter.canWrite(any(), eq(null))).willReturn(true);
given(resourceRegionMessageConverter.canWrite(any(), eq(APPLICATION_OCTET_STREAM))).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest);
then(resourceRegionMessageConverter).should(never()).write(anyCollection(), any(), any());
assertThat(servletResponse.getStatus()).isEqualTo(206);
}
@Test //SPR-14767
public void shouldHandleValidatorHeadersInputResponses() throws Exception {
servletRequest.setMethod("PUT");
String etagValue = "\"some-etag\"";
ResponseEntity<String> returnValue = ResponseEntity.ok().header(HttpHeaders.ETAG, etagValue).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.OK, "body", etagValue, -1);
}
@Test
public void shouldNotFailPreconditionForPutRequests() throws Exception {
servletRequest.setMethod("PUT");
ZonedDateTime dateTime = ofEpochMilli(new Date().getTime()).atZone(GMT);
servletRequest.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, RFC_1123_DATE_TIME.format(dateTime));
long justModified = dateTime.plus(1, ChronoUnit.SECONDS).toEpochSecond() * 1000;
ResponseEntity<String> returnValue = ResponseEntity.ok()
.lastModified(justModified).body("body");
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertConditionalResponse(HttpStatus.OK, null, null, justModified);
}
@Test
public void varyHeader() throws Exception {
String[] entityValues = {"Accept-Language", "User-Agent"};
String[] existingValues = {};
String[] expected = {"Accept-Language, User-Agent"};
testVaryHeader(entityValues, existingValues, expected);
}
@Test
public void varyHeaderWithExistingWildcard() throws Exception {
String[] entityValues = {"Accept-Language"};
String[] existingValues = {"*"};
String[] expected = {"*"};
testVaryHeader(entityValues, existingValues, expected);
}
@Test
public void varyHeaderWithExistingCommaValues() throws Exception {
String[] entityValues = {"Accept-Language", "User-Agent"};
String[] existingValues = {"Accept-Encoding", "Accept-Language"};
String[] expected = {"Accept-Encoding", "Accept-Language", "User-Agent"};
testVaryHeader(entityValues, existingValues, expected);
}
@Test
public void varyHeaderWithExistingCommaSeparatedValues() throws Exception {
String[] entityValues = {"Accept-Language", "User-Agent"};
String[] existingValues = {"Accept-Encoding, Accept-Language"};
String[] expected = {"Accept-Encoding, Accept-Language", "User-Agent"};
testVaryHeader(entityValues, existingValues, expected);
}
@Test
public void handleReturnValueVaryHeader() throws Exception {
String[] entityValues = {"Accept-Language", "User-Agent"};
String[] existingValues = {"Accept-Encoding, Accept-Language"};
String[] expected = {"Accept-Encoding, Accept-Language", "User-Agent"};
testVaryHeader(entityValues, existingValues, expected);
}
private void testVaryHeader(String[] entityValues, String[] existingValues, String[] expected) throws Exception {
ResponseEntity<String> returnValue = ResponseEntity.ok().varyBy(entityValues).body("Foo");
for (String value : existingValues) {
servletResponse.addHeader("Vary", value);
}
initStringMessageConversion(TEXT_PLAIN);
processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest);
assertThat(mavContainer.isRequestHandled()).isTrue();
assertThat(servletResponse.getHeaders("Vary")).isEqualTo(Arrays.asList(expected));
verify(stringHttpMessageConverter).write(eq("Foo"), eq(TEXT_PLAIN), isA(HttpOutputMessage.class));
}
private void initStringMessageConversion(MediaType accepted) {
given(stringHttpMessageConverter.canWrite(String.class, null)).willReturn(true);
given(stringHttpMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(TEXT_PLAIN));
given(stringHttpMessageConverter.getSupportedMediaTypes(String.class)).willReturn(Collections.singletonList(TEXT_PLAIN));
given(stringHttpMessageConverter.canWrite(String.class, accepted)).willReturn(true);
}
private void assertResponseBody(String body) throws IOException {
ArgumentCaptor<HttpOutputMessage> outputMessage = ArgumentCaptor.forClass(HttpOutputMessage.class);
verify(stringHttpMessageConverter).write(eq(body), eq(TEXT_PLAIN), outputMessage.capture());
}
private void assertConditionalResponse(HttpStatus status, String body, String etag, long lastModified)
throws IOException {
assertThat(servletResponse.getStatus()).isEqualTo(status.value());
assertThat(mavContainer.isRequestHandled()).isTrue();
if (body != null) {
assertResponseBody(body);
}
else {
assertThat(servletResponse.getContentAsByteArray().length).isEqualTo(0);
}
if (etag != null) {
assertThat(servletResponse.getHeaderValues(HttpHeaders.ETAG).size()).isEqualTo(1);
assertThat(servletResponse.getHeader(HttpHeaders.ETAG)).isEqualTo(etag);
}
if (lastModified != -1) {
assertThat(servletResponse.getHeaderValues(HttpHeaders.LAST_MODIFIED).size()).isEqualTo(1);
assertThat((servletResponse.getDateHeader(HttpHeaders.LAST_MODIFIED) / 1000)).isEqualTo((lastModified / 1000));
}
}
@SuppressWarnings("unused")
public ResponseEntity<String> handle1(HttpEntity<String> httpEntity, ResponseEntity<String> entity,
int i, RequestEntity<String> requestEntity) {
return entity;
}
@SuppressWarnings("unused")
public HttpEntity<?> handle2(HttpEntity<?> entity) {
return entity;
}
@SuppressWarnings("unused")
public CustomHttpEntity handle2x(HttpEntity<?> entity) {
return new CustomHttpEntity();
}
@SuppressWarnings("unused")
public int handle3() {
return 42;
}
@SuppressWarnings("unused")
@RequestMapping(produces = {"text/html", "application/xhtml+xml"})
public ResponseEntity<String> handle4() {
return null;
}
@SuppressWarnings("unused")
public ResponseEntity<Resource> handle5() {
return null;
}
@SuppressWarnings("unused")
public ErrorResponse handle6() {
return null;
}
@SuppressWarnings("unused")
public ProblemDetail handle7() {
return null;
}
@SuppressWarnings("unused")
public static class CustomHttpEntity extends HttpEntity<Object> {
}
}
相关信息
相关文章
spring AbstractRequestAttributesArgumentResolverTests 源码
spring AbstractServletHandlerMethodTests 源码
spring DeferredResultReturnValueHandlerTests 源码
spring ExceptionHandlerExceptionResolverTests 源码
spring ExtendedServletRequestDataBinderTests 源码
spring HandlerMethodAnnotationDetectionTests 源码
spring HttpEntityMethodProcessorTests 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦