001/*
002 * Copyright 2012-2018 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 *      http://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.boot.autoconfigure.web.servlet.error;
018
019import java.util.Collections;
020import java.util.List;
021import java.util.Map;
022
023import javax.servlet.http.HttpServletRequest;
024import javax.servlet.http.HttpServletResponse;
025
026import org.springframework.boot.autoconfigure.web.ErrorProperties;
027import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
028import org.springframework.boot.web.servlet.error.ErrorAttributes;
029import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
030import org.springframework.http.HttpStatus;
031import org.springframework.http.MediaType;
032import org.springframework.http.ResponseEntity;
033import org.springframework.stereotype.Controller;
034import org.springframework.util.Assert;
035import org.springframework.web.bind.annotation.RequestMapping;
036import org.springframework.web.servlet.ModelAndView;
037
038/**
039 * Basic global error {@link Controller}, rendering {@link ErrorAttributes}. More specific
040 * errors can be handled either using Spring MVC abstractions (e.g.
041 * {@code @ExceptionHandler}) or by adding servlet
042 * {@link AbstractServletWebServerFactory#setErrorPages server error pages}.
043 *
044 * @author Dave Syer
045 * @author Phillip Webb
046 * @author Michael Stummvoll
047 * @author Stephane Nicoll
048 * @see ErrorAttributes
049 * @see ErrorProperties
050 */
051@Controller
052@RequestMapping("${server.error.path:${error.path:/error}}")
053public class BasicErrorController extends AbstractErrorController {
054
055        private final ErrorProperties errorProperties;
056
057        /**
058         * Create a new {@link BasicErrorController} instance.
059         * @param errorAttributes the error attributes
060         * @param errorProperties configuration properties
061         */
062        public BasicErrorController(ErrorAttributes errorAttributes,
063                        ErrorProperties errorProperties) {
064                this(errorAttributes, errorProperties, Collections.emptyList());
065        }
066
067        /**
068         * Create a new {@link BasicErrorController} instance.
069         * @param errorAttributes the error attributes
070         * @param errorProperties configuration properties
071         * @param errorViewResolvers error view resolvers
072         */
073        public BasicErrorController(ErrorAttributes errorAttributes,
074                        ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
075                super(errorAttributes, errorViewResolvers);
076                Assert.notNull(errorProperties, "ErrorProperties must not be null");
077                this.errorProperties = errorProperties;
078        }
079
080        @Override
081        public String getErrorPath() {
082                return this.errorProperties.getPath();
083        }
084
085        @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
086        public ModelAndView errorHtml(HttpServletRequest request,
087                        HttpServletResponse response) {
088                HttpStatus status = getStatus(request);
089                Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
090                                request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
091                response.setStatus(status.value());
092                ModelAndView modelAndView = resolveErrorView(request, response, status, model);
093                return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
094        }
095
096        @RequestMapping
097        public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
098                Map<String, Object> body = getErrorAttributes(request,
099                                isIncludeStackTrace(request, MediaType.ALL));
100                HttpStatus status = getStatus(request);
101                return new ResponseEntity<>(body, status);
102        }
103
104        /**
105         * Determine if the stacktrace attribute should be included.
106         * @param request the source request
107         * @param produces the media type produced (or {@code MediaType.ALL})
108         * @return if the stacktrace attribute should be included
109         */
110        protected boolean isIncludeStackTrace(HttpServletRequest request,
111                        MediaType produces) {
112                IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
113                if (include == IncludeStacktrace.ALWAYS) {
114                        return true;
115                }
116                if (include == IncludeStacktrace.ON_TRACE_PARAM) {
117                        return getTraceParameter(request);
118                }
119                return false;
120        }
121
122        /**
123         * Provide access to the error properties.
124         * @return the error properties
125         */
126        protected ErrorProperties getErrorProperties() {
127                return this.errorProperties;
128        }
129
130}