spring RequestMappingHandlerAdapterTests 源码
spring RequestMappingHandlerAdapterTests 代码
文件路径:/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterTests.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.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ModelMethodProcessor;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link RequestMappingHandlerAdapter}.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @see ServletAnnotationControllerHandlerMethodTests
* @see HandlerMethodAnnotationDetectionTests
* @see RequestMappingHandlerAdapterIntegrationTests
*/
public class RequestMappingHandlerAdapterTests {
private static int RESOLVER_COUNT;
private static int INIT_BINDER_RESOLVER_COUNT;
private static int HANDLER_COUNT;
private RequestMappingHandlerAdapter handlerAdapter;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private StaticWebApplicationContext webAppContext;
@BeforeAll
public static void setupOnce() {
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setApplicationContext(new StaticWebApplicationContext());
adapter.afterPropertiesSet();
RESOLVER_COUNT = adapter.getArgumentResolvers().size();
INIT_BINDER_RESOLVER_COUNT = adapter.getInitBinderArgumentResolvers().size();
HANDLER_COUNT = adapter.getReturnValueHandlers().size();
}
@BeforeEach
public void setup() throws Exception {
this.webAppContext = new StaticWebApplicationContext();
this.handlerAdapter = new RequestMappingHandlerAdapter();
this.handlerAdapter.setApplicationContext(this.webAppContext);
this.request = new MockHttpServletRequest("GET", "/");
this.response = new MockHttpServletResponse();
}
@Test
public void cacheControlWithoutSessionAttributes() throws Exception {
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
this.handlerAdapter.setCacheSeconds(100);
this.handlerAdapter.afterPropertiesSet();
this.handlerAdapter.handle(this.request, this.response, handlerMethod);
assertThat(response.getHeader("Cache-Control").contains("max-age")).isTrue();
}
@Test
public void cacheControlWithSessionAttributes() throws Exception {
SessionAttributeController handler = new SessionAttributeController();
this.handlerAdapter.setCacheSeconds(100);
this.handlerAdapter.afterPropertiesSet();
this.handlerAdapter.handle(this.request, this.response, handlerMethod(handler, "handle"));
assertThat(this.response.getHeader("Cache-Control")).isEqualTo("no-store");
}
@Test
public void setAlwaysUseRedirectAttributes() throws Exception {
HandlerMethodArgumentResolver redirectAttributesResolver = new RedirectAttributesMethodArgumentResolver();
HandlerMethodArgumentResolver modelResolver = new ModelMethodProcessor();
HandlerMethodReturnValueHandler viewHandler = new ViewNameMethodReturnValueHandler();
this.handlerAdapter.setArgumentResolvers(Arrays.asList(redirectAttributesResolver, modelResolver));
this.handlerAdapter.setReturnValueHandlers(Collections.singletonList(viewHandler));
this.handlerAdapter.setIgnoreDefaultModelOnRedirect(true);
this.handlerAdapter.afterPropertiesSet();
this.request.setAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
HandlerMethod handlerMethod = handlerMethod(new RedirectAttributeController(), "handle", Model.class);
ModelAndView mav = this.handlerAdapter.handle(request, response, handlerMethod);
assertThat(mav.getModel().isEmpty()).as("Without RedirectAttributes arg, model should be empty").isTrue();
}
@Test
public void setCustomArgumentResolvers() throws Exception {
HandlerMethodArgumentResolver resolver = new ServletRequestMethodArgumentResolver();
this.handlerAdapter.setCustomArgumentResolvers(Collections.singletonList(resolver));
this.handlerAdapter.afterPropertiesSet();
assertThat(this.handlerAdapter.getArgumentResolvers().contains(resolver)).isTrue();
assertMethodProcessorCount(RESOLVER_COUNT + 1, INIT_BINDER_RESOLVER_COUNT + 1, HANDLER_COUNT);
}
@Test
public void setArgumentResolvers() throws Exception {
HandlerMethodArgumentResolver resolver = new ServletRequestMethodArgumentResolver();
this.handlerAdapter.setArgumentResolvers(Collections.singletonList(resolver));
this.handlerAdapter.afterPropertiesSet();
assertMethodProcessorCount(1, INIT_BINDER_RESOLVER_COUNT, HANDLER_COUNT);
}
@Test
public void setInitBinderArgumentResolvers() throws Exception {
HandlerMethodArgumentResolver resolver = new ServletRequestMethodArgumentResolver();
this.handlerAdapter.setInitBinderArgumentResolvers(Collections.singletonList(resolver));
this.handlerAdapter.afterPropertiesSet();
assertMethodProcessorCount(RESOLVER_COUNT, 1, HANDLER_COUNT);
}
@Test
public void setCustomReturnValueHandlers() {
HandlerMethodReturnValueHandler handler = new ViewNameMethodReturnValueHandler();
this.handlerAdapter.setCustomReturnValueHandlers(Collections.singletonList(handler));
this.handlerAdapter.afterPropertiesSet();
assertThat(this.handlerAdapter.getReturnValueHandlers().contains(handler)).isTrue();
assertMethodProcessorCount(RESOLVER_COUNT, INIT_BINDER_RESOLVER_COUNT, HANDLER_COUNT + 1);
}
@Test
public void setReturnValueHandlers() {
HandlerMethodReturnValueHandler handler = new ModelMethodProcessor();
this.handlerAdapter.setReturnValueHandlers(Collections.singletonList(handler));
this.handlerAdapter.afterPropertiesSet();
assertMethodProcessorCount(RESOLVER_COUNT, INIT_BINDER_RESOLVER_COUNT, 1);
}
@Test
public void modelAttributeAdvice() throws Exception {
this.webAppContext.registerSingleton("maa", ModelAttributeAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
this.handlerAdapter.afterPropertiesSet();
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
assertThat(mav.getModel().get("attr1")).isEqualTo("lAttr1");
assertThat(mav.getModel().get("attr2")).isEqualTo("gAttr2");
}
@Test
public void prototypeControllerAdvice() throws Exception {
this.webAppContext.registerPrototype("maa", ModelAttributeAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
this.handlerAdapter.afterPropertiesSet();
Map<String, Object> model1 = this.handlerAdapter.handle(this.request, this.response, handlerMethod).getModel();
Map<String, Object> model2 = this.handlerAdapter.handle(this.request, this.response, handlerMethod).getModel();
assertThat(model1.get("instance")).isNotSameAs(model2.get("instance"));
}
@Test
public void modelAttributeAdviceInParentContext() throws Exception {
StaticWebApplicationContext parent = new StaticWebApplicationContext();
parent.registerSingleton("maa", ModelAttributeAdvice.class);
parent.refresh();
this.webAppContext.setParent(parent);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
this.handlerAdapter.afterPropertiesSet();
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
assertThat(mav.getModel().get("attr1")).isEqualTo("lAttr1");
assertThat(mav.getModel().get("attr2")).isEqualTo("gAttr2");
}
@Test
public void modelAttributePackageNameAdvice() throws Exception {
this.webAppContext.registerSingleton("mapa", ModelAttributePackageAdvice.class);
this.webAppContext.registerSingleton("manupa", ModelAttributeNotUsedPackageAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
this.handlerAdapter.afterPropertiesSet();
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
assertThat(mav.getModel().get("attr1")).isEqualTo("lAttr1");
assertThat(mav.getModel().get("attr2")).isEqualTo("gAttr2");
assertThat(mav.getModel().get("attr3")).isNull();
}
// SPR-10859
@Test
public void responseBodyAdvice() throws Exception {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
this.handlerAdapter.setMessageConverters(converters);
this.webAppContext.registerSingleton("rba", ResponseCodeSuppressingAdvice.class);
this.webAppContext.refresh();
this.request.addHeader("Accept", MediaType.APPLICATION_JSON_VALUE);
this.request.setParameter("c", "callback");
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handleBadRequest");
this.handlerAdapter.afterPropertiesSet();
this.handlerAdapter.handle(this.request, this.response, handlerMethod);
assertThat(this.response.getStatus()).isEqualTo(200);
assertThat(this.response.getContentAsString()).isEqualTo("{\"status\":400,\"message\":\"body\"}");
}
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
return new InvocableHandlerMethod(handler, method);
}
private void assertMethodProcessorCount(int resolverCount, int initBinderResolverCount, int handlerCount) {
assertThat(this.handlerAdapter.getArgumentResolvers().size()).isEqualTo(resolverCount);
assertThat(this.handlerAdapter.getInitBinderArgumentResolvers().size()).isEqualTo(initBinderResolverCount);
assertThat(this.handlerAdapter.getReturnValueHandlers().size()).isEqualTo(handlerCount);
}
@SuppressWarnings("unused")
private static class SimpleController {
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("attr1", "lAttr1");
}
public String handle() {
return null;
}
public ResponseEntity<Map<String, String>> handleWithResponseEntity() {
return new ResponseEntity<>(Collections.singletonMap(
"foo", "bar"), HttpStatus.OK);
}
public ResponseEntity<String> handleBadRequest() {
return new ResponseEntity<>("body", HttpStatus.BAD_REQUEST);
}
}
@SessionAttributes("attr1")
private static class SessionAttributeController {
@SuppressWarnings("unused")
public void handle() {
}
}
@SuppressWarnings("unused")
private static class RedirectAttributeController {
public String handle(Model model) {
model.addAttribute("someAttr", "someAttrValue");
return "redirect:/path";
}
}
@ControllerAdvice
private static class ModelAttributeAdvice {
@SuppressWarnings("unused")
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("attr1", "gAttr1");
model.addAttribute("attr2", "gAttr2");
model.addAttribute("instance", this);
}
}
@ControllerAdvice({"org.springframework.web.servlet.mvc.method.annotation", "java.lang"})
private static class ModelAttributePackageAdvice {
@SuppressWarnings("unused")
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("attr2", "gAttr2");
}
}
@ControllerAdvice("java.lang")
private static class ModelAttributeNotUsedPackageAdvice {
@SuppressWarnings("unused")
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("attr3", "gAttr3");
}
}
/**
* This class additionally implements {@link RequestBodyAdvice} solely for the purpose
* of verifying that controller advice implementing both {@link ResponseBodyAdvice}
* and {@link RequestBodyAdvice} does not get registered twice.
*
* @see <a href="https://github.com/spring-projects/spring-framework/pull/22638">gh-22638</a>
*/
@ControllerAdvice
private static class ResponseCodeSuppressingAdvice extends AbstractMappingJacksonResponseBodyAdvice implements RequestBodyAdvice {
@Override
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
int status = ((ServletServerHttpResponse) response).getServletResponse().getStatus();
response.setStatusCode(HttpStatus.OK);
Map<String, Object> map = new LinkedHashMap<>();
map.put("status", status);
map.put("message", bodyContainer.getValue());
bodyContainer.setValue(map);
}
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return StringHttpMessageConverter.class.equals(converterType);
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return inputMessage;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return "default value for empty body";
}
}
}
相关信息
相关文章
spring AbstractRequestAttributesArgumentResolverTests 源码
spring AbstractServletHandlerMethodTests 源码
spring DeferredResultReturnValueHandlerTests 源码
spring ExceptionHandlerExceptionResolverTests 源码
spring ExtendedServletRequestDataBinderTests 源码
spring HandlerMethodAnnotationDetectionTests 源码
spring HttpEntityMethodProcessorMockTests 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦