22
22
import org .springframework .http .HttpStatus ;
23
23
import org .springframework .util .StringUtils ;
24
24
import org .springframework .web .bind .annotation .ResponseStatus ;
25
- import org .springframework .web .context .request .NativeWebRequest ;
26
25
import org .springframework .web .context .request .ServletWebRequest ;
27
- import org .springframework .web .method .support . HandlerMethodArgumentResolver ;
26
+ import org .springframework .web .method .HandlerMethod ;
28
27
import org .springframework .web .method .support .HandlerMethodReturnValueHandler ;
29
28
import org .springframework .web .method .support .HandlerMethodReturnValueHandlerComposite ;
30
29
import org .springframework .web .method .support .InvocableHandlerMethod ;
31
30
import org .springframework .web .method .support .ModelAndViewContainer ;
32
31
import org .springframework .web .servlet .View ;
33
32
34
33
/**
35
- * Extends {@link InvocableHandlerMethod} with the ability to handle the value returned from the method through
36
- * a registered {@link HandlerMethodArgumentResolver} that supports the given return value type.
37
- * Return value handling may include writing to the response or updating the {@link ModelAndViewContainer} structure.
34
+ * Extends {@link InvocableHandlerMethod} with the ability to handle return
35
+ * values through a registered {@link HandlerMethodReturnValueHandler} and
36
+ * also supports setting the response status based on a method-level
37
+ * {@code @ResponseStatus} annotation.
38
38
*
39
- * <p>If the underlying method has a {@link ResponseStatus} instruction, the status on the response is set
40
- * accordingly after the method is invoked but before the return value is handled.
39
+ * <p>A {@code null} return value (including void) may be interpreted as the
40
+ * end of request processing in combination with a {@code @ResponseStatus}
41
+ * annotation, a not-modified check condition
42
+ * (see {@link ServletWebRequest#checkNotModified(long)}), or
43
+ * a method argument that provides access to the response stream.
41
44
*
42
45
* @author Rossen Stoyanchev
43
46
* @since 3.1
44
- * @see #invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...)
45
47
*/
46
48
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
47
49
@@ -51,104 +53,104 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
51
53
52
54
private HandlerMethodReturnValueHandlerComposite returnValueHandlers ;
53
55
54
- public void setHandlerMethodReturnValueHandlers (HandlerMethodReturnValueHandlerComposite returnValueHandlers ) {
55
- this .returnValueHandlers = returnValueHandlers ;
56
- }
57
56
58
57
/**
59
- * Creates a {@link ServletInvocableHandlerMethod} instance with the given bean and method.
60
- * @param handler the object handler
61
- * @param method the method
58
+ * Creates an instance from the given handler and method.
62
59
*/
63
60
public ServletInvocableHandlerMethod (Object handler , Method method ) {
64
61
super (handler , method );
62
+ initResponseStatus ();
63
+ }
64
+
65
+ /**
66
+ * Create an instance from a {@code HandlerMethod}.
67
+ */
68
+ public ServletInvocableHandlerMethod (HandlerMethod handlerMethod ) {
69
+ super (handlerMethod );
70
+ initResponseStatus ();
71
+ }
65
72
66
- ResponseStatus annotation = getMethodAnnotation (ResponseStatus .class );
67
- if (annotation != null ) {
68
- this .responseStatus = annotation .value ();
69
- this .responseReason = annotation .reason ();
73
+ private void initResponseStatus () {
74
+ ResponseStatus annot = getMethodAnnotation (ResponseStatus .class );
75
+ if (annot != null ) {
76
+ this .responseStatus = annot .value ();
77
+ this .responseReason = annot .reason ();
70
78
}
71
79
}
72
80
73
81
/**
74
- * Invokes the method and handles the return value through a registered {@link HandlerMethodReturnValueHandler}.
75
- * <p>Return value handling may be skipped entirely when the method returns {@code null} (also possibly due
76
- * to a {@code void} return type) and one of the following additional conditions is true:
77
- * <ul>
78
- * <li>A {@link HandlerMethodArgumentResolver} has set the {@link ModelAndViewContainer#setRequestHandled(boolean)}
79
- * flag to {@code false} -- e.g. method arguments providing access to the response.
80
- * <li>The request qualifies as "not modified" as defined in {@link ServletWebRequest#checkNotModified(long)}
81
- * and {@link ServletWebRequest#checkNotModified(String)}. In this case a response with "not modified" response
82
- * headers will be automatically generated without the need for return value handling.
83
- * <li>The status on the response is set due to a @{@link ResponseStatus} instruction.
84
- * </ul>
85
- * <p>After the return value is handled, callers of this method can use the {@link ModelAndViewContainer}
86
- * to gain access to model attributes, view selection choices, and to check if view resolution is even needed.
82
+ * Register {@link HandlerMethodReturnValueHandler} instances to use to
83
+ * handle return values.
84
+ */
85
+ public void setHandlerMethodReturnValueHandlers (HandlerMethodReturnValueHandlerComposite returnValueHandlers ) {
86
+ this .returnValueHandlers = returnValueHandlers ;
87
+ }
88
+
89
+ /**
90
+ * Invokes the method and handles the return value through a registered
91
+ * {@link HandlerMethodReturnValueHandler}.
87
92
*
88
- * @param request the current request
89
- * @param mavContainer the {@link ModelAndViewContainer} for the current request
90
- * @param providedArgs argument values to try to use without the need for view resolution
93
+ * @param webRequest the current request
94
+ * @param mavContainer the ModelAndViewContainer for this request
95
+ * @param providedArgs "given" arguments matched by type, not resolved
91
96
*/
92
- public final void invokeAndHandle (
93
- NativeWebRequest request , ModelAndViewContainer mavContainer ,
94
- Object ... providedArgs ) throws Exception {
97
+ public final void invokeAndHandle (ServletWebRequest webRequest ,
98
+ ModelAndViewContainer mavContainer , Object ... providedArgs ) throws Exception {
95
99
96
- Object returnValue = invokeForRequest (request , mavContainer , providedArgs );
100
+ Object returnValue = invokeForRequest (webRequest , mavContainer , providedArgs );
97
101
98
- setResponseStatus (( ServletWebRequest ) request );
102
+ setResponseStatus (webRequest );
99
103
100
104
if (returnValue == null ) {
101
- if (isRequestNotModified (request ) || hasResponseStatus () || mavContainer .isRequestHandled ()) {
105
+ if (isRequestNotModified (webRequest ) || hasResponseStatus () || mavContainer .isRequestHandled ()) {
102
106
mavContainer .setRequestHandled (true );
103
107
return ;
104
108
}
105
109
}
110
+ else if (StringUtils .hasText (this .responseReason )) {
111
+ mavContainer .setRequestHandled (true );
112
+ return ;
113
+ }
106
114
107
115
mavContainer .setRequestHandled (false );
108
116
109
117
try {
110
- returnValueHandlers .handleReturnValue (returnValue , getReturnType (), mavContainer , request );
111
- } catch (Exception ex ) {
118
+ this .returnValueHandlers .handleReturnValue (returnValue , getReturnValueType (returnValue ), mavContainer , webRequest );
119
+ }
120
+ catch (Exception ex ) {
112
121
if (logger .isTraceEnabled ()) {
113
122
logger .trace (getReturnValueHandlingErrorMessage ("Error handling return value" , returnValue ), ex );
114
123
}
115
124
throw ex ;
116
125
}
117
126
}
118
127
119
- private String getReturnValueHandlingErrorMessage (String message , Object returnValue ) {
120
- StringBuilder sb = new StringBuilder (message );
121
- if (returnValue != null ) {
122
- sb .append (" [type=" + returnValue .getClass ().getName () + "] " );
123
- }
124
- sb .append ("[value=" + returnValue + "]" );
125
- return getDetailedErrorMessage (sb .toString ());
126
- }
127
-
128
128
/**
129
129
* Set the response status according to the {@link ResponseStatus} annotation.
130
130
*/
131
131
private void setResponseStatus (ServletWebRequest webRequest ) throws IOException {
132
- if (this .responseStatus != null ) {
133
- if (StringUtils .hasText (this .responseReason )) {
134
- webRequest .getResponse ().sendError (this .responseStatus .value (), this .responseReason );
135
- }
136
- else {
137
- webRequest .getResponse ().setStatus (this .responseStatus .value ());
138
- }
132
+ if (this .responseStatus == null ) {
133
+ return ;
134
+ }
139
135
140
- // to be picked up by the RedirectView
141
- webRequest .getRequest ().setAttribute ( View . RESPONSE_STATUS_ATTRIBUTE , this .responseStatus );
136
+ if ( StringUtils . hasText ( this . responseReason )) {
137
+ webRequest .getResponse ().sendError ( this . responseStatus . value () , this .responseReason );
142
138
}
139
+ else {
140
+ webRequest .getResponse ().setStatus (this .responseStatus .value ());
141
+ }
142
+
143
+ // to be picked up by the RedirectView
144
+ webRequest .getRequest ().setAttribute (View .RESPONSE_STATUS_ATTRIBUTE , this .responseStatus );
143
145
}
144
146
145
147
/**
146
148
* Does the given request qualify as "not modified"?
147
149
* @see ServletWebRequest#checkNotModified(long)
148
150
* @see ServletWebRequest#checkNotModified(String)
149
151
*/
150
- private boolean isRequestNotModified (NativeWebRequest request ) {
151
- return (( ServletWebRequest ) request ) .isNotModified ();
152
+ private boolean isRequestNotModified (ServletWebRequest webRequest ) {
153
+ return webRequest .isNotModified ();
152
154
}
153
155
154
156
/**
@@ -157,4 +159,14 @@ private boolean isRequestNotModified(NativeWebRequest request) {
157
159
private boolean hasResponseStatus () {
158
160
return responseStatus != null ;
159
161
}
162
+
163
+ private String getReturnValueHandlingErrorMessage (String message , Object returnValue ) {
164
+ StringBuilder sb = new StringBuilder (message );
165
+ if (returnValue != null ) {
166
+ sb .append (" [type=" + returnValue .getClass ().getName () + "] " );
167
+ }
168
+ sb .append ("[value=" + returnValue + "]" );
169
+ return getDetailedErrorMessage (sb .toString ());
170
+ }
171
+
160
172
}
0 commit comments