From 73a794336cceb543098b73ae57cc0393bd401c00 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 15 Jan 2016 17:15:34 -0500 Subject: [PATCH] Support @ResponseStatus on controller type level Issue: SPR-13547 --- .../web/bind/annotation/ResponseStatus.java | 6 +++- .../ServletInvocableHandlerMethod.java | 6 +++- .../ServletInvocableHandlerMethodTests.java | 32 +++++++++++++++---- src/asciidoc/whats-new.adoc | 1 + 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/ResponseStatus.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/ResponseStatus.java index 8ad9c1e9b5..8843eb1266 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/ResponseStatus.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/ResponseStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -44,6 +44,10 @@ import org.springframework.http.HttpStatus; * preferable to use a {@link org.springframework.http.ResponseEntity} as * a return type and avoid the use of {@code @ResponseStatus} altogether. * + *

Note that a controller class may also be annotated with + * {@code @ResponseStatus} and is then inherited by all {@code @RequestMapping} + * methods. + * * @author Arjen Poutsma * @author Sam Brannen * @see org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java index 05cbba3c09..8044d66412 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -24,6 +24,7 @@ import java.util.concurrent.Callable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpStatus; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -82,6 +83,9 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { private void initResponseStatus() { ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class); + if (annotation == null) { + annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class); + } if (annotation != null) { this.responseStatus = annotation.code(); this.responseReason = annotation.reason(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java index 431e4d5ccf..b1de33efb3 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -92,6 +92,15 @@ public class ServletInvocableHandlerMethodTests { assertEquals(HttpStatus.BAD_REQUEST.value(), this.response.getStatus()); } + @Test + public void invokeAndHandle_VoidWithTypeLevelResponseStatus() throws Exception { + ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(new ResponseStatusHandler(), "handle"); + handlerMethod.invokeAndHandle(this.webRequest, this.mavContainer); + + assertTrue(this.mavContainer.isRequestHandled()); + assertEquals(HttpStatus.BAD_REQUEST.value(), this.response.getStatus()); + } + @Test public void invokeAndHandle_VoidWithHttpServletResponseArgument() throws Exception { this.argumentResolvers.addResolver(new ServletResponseMethodArgumentResolver()); @@ -169,7 +178,7 @@ public class ServletInvocableHandlerMethodTests { } private void wrapConcurrentResult_ResponseBody(Object handler) throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); this.returnValueHandlers.addHandler(new RequestResponseBodyMethodProcessor(converters)); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(handler, "handle"); @@ -181,7 +190,7 @@ public class ServletInvocableHandlerMethodTests { @Test public void wrapConcurrentResult_ResponseEntity() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); this.returnValueHandlers.addHandler(new HttpEntityMethodProcessor(converters)); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(new ResponseEntityHandler(), "handleDeferred"); @@ -195,7 +204,7 @@ public class ServletInvocableHandlerMethodTests { @Test public void wrapConcurrentResult_ResponseEntityNullBody() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); List advice = Collections.singletonList(mock(ResponseBodyAdvice.class)); HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters, null, advice); @@ -210,7 +219,7 @@ public class ServletInvocableHandlerMethodTests { @Test public void wrapConcurrentResult_ResponseEntityNullReturnValue() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); List advice = Collections.singletonList(mock(ResponseBodyAdvice.class)); HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters, null, advice); @@ -225,7 +234,7 @@ public class ServletInvocableHandlerMethodTests { @Test public void wrapConcurrentResult_ResponseBodyEmitter() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); this.returnValueHandlers.addHandler(new ResponseBodyEmitterReturnValueHandler(converters)); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(new AsyncHandler(), "handleWithEmitter"); @@ -272,6 +281,7 @@ public class ServletInvocableHandlerMethodTests { return handlerMethod; } + @SuppressWarnings("unused") @ResponseStatus @Retention(RetentionPolicy.RUNTIME) @interface ComposedResponseStatus { @@ -311,6 +321,14 @@ public class ServletInvocableHandlerMethodTests { } } + @SuppressWarnings("unused") + @ResponseStatus(HttpStatus.BAD_REQUEST) + private static class ResponseStatusHandler { + + public void handle() { + } + } + private static class MethodLevelResponseBodyHandler { @ResponseBody @@ -324,7 +342,7 @@ public class ServletInvocableHandlerMethodTests { private static class TypeLevelResponseBodyHandler { public DeferredResult handle() { - return new DeferredResult(); + return new DeferredResult<>(); } } diff --git a/src/asciidoc/whats-new.adoc b/src/asciidoc/whats-new.adoc index abf5b79bb3..59b95d88ea 100644 --- a/src/asciidoc/whats-new.adoc +++ b/src/asciidoc/whats-new.adoc @@ -653,6 +653,7 @@ Spring 4.3 also improves the caching abstraction as follows: === Web Improvements * New `@RestControllerAdvice` annotation combines `@ControllerAdvice` with `@ResponseBody`. +* `@ResponseStatus` can be used on a controller type is inherited for all method. * `AsyncRestTemplate` supports request interception. === Testing Improvements