001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.mvc;
018
019import javax.servlet.http.HttpServletRequest;
020import javax.servlet.http.HttpServletResponse;
021
022import org.springframework.http.HttpMethod;
023import org.springframework.http.HttpStatus;
024import org.springframework.lang.Nullable;
025import org.springframework.web.servlet.ModelAndView;
026import org.springframework.web.servlet.View;
027import org.springframework.web.servlet.support.RequestContextUtils;
028
029/**
030 * Trivial controller that always returns a pre-configured view and optionally
031 * sets the response status code. The view and status can be configured using
032 * the provided configuration properties.
033 *
034 * @author Rod Johnson
035 * @author Juergen Hoeller
036 * @author Keith Donald
037 * @author Rossen Stoyanchev
038 */
039public class ParameterizableViewController extends AbstractController {
040
041        @Nullable
042        private Object view;
043
044        @Nullable
045        private HttpStatus statusCode;
046
047        private boolean statusOnly;
048
049
050        public ParameterizableViewController() {
051                super(false);
052                setSupportedMethods(HttpMethod.GET.name(), HttpMethod.HEAD.name());
053        }
054
055        /**
056         * Set a view name for the ModelAndView to return, to be resolved by the
057         * DispatcherServlet via a ViewResolver. Will override any pre-existing
058         * view name or View.
059         */
060        public void setViewName(@Nullable String viewName) {
061                this.view = viewName;
062        }
063
064        /**
065         * Return the name of the view to delegate to, or {@code null} if using a
066         * View instance.
067         */
068        @Nullable
069        public String getViewName() {
070                if (this.view instanceof String) {
071                        String viewName = (String) this.view;
072                        if (getStatusCode() != null && getStatusCode().is3xxRedirection()) {
073                                return viewName.startsWith("redirect:") ? viewName : "redirect:" + viewName;
074                        }
075                        else {
076                                return viewName;
077                        }
078                }
079                return null;
080        }
081
082        /**
083         * Set a View object for the ModelAndView to return.
084         * Will override any pre-existing view name or View.
085         * @since 4.1
086         */
087        public void setView(View view) {
088                this.view = view;
089        }
090
091        /**
092         * Return the View object, or {@code null} if we are using a view name
093         * to be resolved by the DispatcherServlet via a ViewResolver.
094         * @since 4.1
095         */
096        @Nullable
097        public View getView() {
098                return (this.view instanceof View ? (View) this.view : null);
099        }
100
101        /**
102         * Configure the HTTP status code that this controller should set on the
103         * response.
104         * <p>When a "redirect:" prefixed view name is configured, there is no need
105         * to set this property since RedirectView will do that. However this property
106         * may still be used to override the 3xx status code of {@code RedirectView}.
107         * For full control over redirecting provide a {@code RedirectView} instance.
108         * <p>If the status code is 204 and no view is configured, the request is
109         * fully handled within the controller.
110         * @since 4.1
111         */
112        public void setStatusCode(@Nullable HttpStatus statusCode) {
113                this.statusCode = statusCode;
114        }
115
116        /**
117         * Return the configured HTTP status code or {@code null}.
118         * @since 4.1
119         */
120        @Nullable
121        public HttpStatus getStatusCode() {
122                return this.statusCode;
123        }
124
125
126        /**
127         * The property can be used to indicate the request is considered fully
128         * handled within the controller and that no view should be used for rendering.
129         * Useful in combination with {@link #setStatusCode}.
130         * <p>By default this is set to {@code false}.
131         * @since 4.1
132         */
133        public void setStatusOnly(boolean statusOnly) {
134                this.statusOnly = statusOnly;
135        }
136
137        /**
138         * Whether the request is fully handled within the controller.
139         */
140        public boolean isStatusOnly() {
141                return this.statusOnly;
142        }
143
144
145        /**
146         * Return a ModelAndView object with the specified view name.
147         * <p>The content of the {@link RequestContextUtils#getInputFlashMap
148         * "input" FlashMap} is also added to the model.
149         * @see #getViewName()
150         */
151        @Override
152        protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
153                        throws Exception {
154
155                String viewName = getViewName();
156
157                if (getStatusCode() != null) {
158                        if (getStatusCode().is3xxRedirection()) {
159                                request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, getStatusCode());
160                        }
161                        else {
162                                response.setStatus(getStatusCode().value());
163                                if (getStatusCode().equals(HttpStatus.NO_CONTENT) && viewName == null) {
164                                        return null;
165                                }
166                        }
167                }
168
169                if (isStatusOnly()) {
170                        return null;
171                }
172
173                ModelAndView modelAndView = new ModelAndView();
174                modelAndView.addAllObjects(RequestContextUtils.getInputFlashMap(request));
175                if (viewName != null) {
176                        modelAndView.setViewName(viewName);
177                }
178                else {
179                        modelAndView.setView(getView());
180                }
181                return modelAndView;
182        }
183
184        @Override
185        public String toString() {
186                return "ParameterizableViewController [" + formatStatusAndView() + "]";
187        }
188
189        private String formatStatusAndView() {
190                StringBuilder sb = new StringBuilder();
191                if (this.statusCode != null) {
192                        sb.append("status=").append(this.statusCode);
193                }
194                if (this.view != null) {
195                        sb.append(sb.length() != 0 ? ", " : "");
196                        String viewName = getViewName();
197                        sb.append("view=").append(viewName != null ? "\"" + viewName + "\"" : this.view);
198                }
199                return sb.toString();
200        }
201}