基于Spring Boot控制器异常转发实现的全局异常处理
1、原理
在Spring Boot项目中所有控制器出现的异常都会默认交由/error来处理,所以我们可以将/error映射到指定的控制器来处理Spring Boot项目的所有控制器异常。继承org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController类并编写相关逻辑代码即可。
2、系统状态枚举类
package com.drsanjun.enums;
/**
* 服务器状态枚举
*
* @author dyw
* @date 2019年9月25日
*/
public enum SystemStatusEnum {
SUCCEED(0, "成功"), UN_SUCCEED(1, "失败");
/** 枚举键 */
private final Integer key;
/** 枚举值 */
private final String value;
private SystemStatusEnum(Integer key, String value) {
this.key = key;
this.value = value;
}
public Integer getStatue() {
return key;
}
public String getValue() {
return value;
}
}
2、统一接口返回对象
package com.drsanjun.pojo.bean;
import java.io.Serializable;
import com.drsanjun.enums.SystemStatusEnum;
/**
* RESTful接口消息传送实体
*
* @author dyw
* @date 2019年6月15日
*/
public class Message implements Serializable {
private static final long serialVersionUID = 2524200426257142759L;
/** 状态码 */
private Integer statue;
/** 消息 */
private String message;
/** 数据 */
private Object data;
private Message() {
}
private Message(Integer statue, String message) {
this.statue = statue;
this.message = message;
}
private Message(Integer statue, String message, Object data) {
this.statue = statue;
this.message = message;
this.data = data;
}
private Message(SystemStatusEnum statusEnum) {
this.statue = statusEnum.getStatue();
this.message = statusEnum.getValue();
}
private Message(SystemStatusEnum statusEnum, Object data) {
this.statue = statusEnum.getStatue();
this.message = statusEnum.getValue();
this.data = data;
}
/**
* 返回成功状态
*
* @return
*/
public static Message success() {
return new Message(SystemStatusEnum.SUCCEED);
}
/**
* 返回成功状态,自定义提示消息
*
* @return
*/
public static Message success(String message) {
return new Message(SystemStatusEnum.SUCCEED.getStatue(), message);
}
/**
* 返回成功状态,包含数据
*
* @return
*/
public static Message success(Object data) {
return new Message(SystemStatusEnum.SUCCEED, data);
}
/**
* 返回失败状态
*
* @return
*/
public static Message failure() {
return new Message(SystemStatusEnum.UN_SUCCEED);
}
/**
* 返回失败状态,自定义提示消息
*
* @return
*/
public static Message failure(String message) {
return new Message(SystemStatusEnum.UN_SUCCEED.getStatue(), message);
}
/**
* 返回失败状态,包含数据
*
* @return
*/
public static Message failure(Object data) {
return new Message(SystemStatusEnum.UN_SUCCEED, data);
}
/**
* 完全自定义
*
* @return
*/
public static Message message(Integer statue, String message, Object data) {
return new Message(statue, message, data);
}
public Integer getStatue() {
return statue;
}
public void setStatue(Integer statue) {
this.statue = statue;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return "Message {statue=" + statue + ", message=" + message + ", data=" + data + "}";
}
}
3、全局异常处理控制器
package com.drsanjun.controller;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.drsanjun.enums.SystemStatusEnum;
import com.drsanjun.pojo.bean.Message;
/**
* 全局通用异常处理控制器
*
* @author dyw
* @date 2019年9月28日
*/
@RestController
public class ErrorController extends AbstractErrorController {
protected Logger logger = LoggerFactory.getLogger(getClass());
public ErrorController() {
super(new DefaultErrorAttributes());
}
/**
* 所有控制器异常后到会转发到此接口
*/
@Override
public String getErrorPath() {
return null;
}
/**
* 异常处理接口
*
* @param request
* @param response
* @return
*/
@RequestMapping("/error")
public Message error(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, false));
/**
* 获取异常、HTTP状态码、异常类名
*/
Throwable cause = getCause(request);
int httpStatus = (Integer) model.get("status");
String errorClassName = cause.getClass().getName();
/**
* 获取错误信息
*/
String errorMessage = (String) model.get("message");
/**
* 获取友好提示信息
*/
String friendlyMessage = getFriendlyMessage(cause);
/**
* 设置HTTP状态码
*/
response.setStatus(httpStatus);
/**
* 封装返回数据
*/
Map<String, Object> resultMap = new HashMap<String, Object>();
resultMap.put("errorMessage", errorMessage);
resultMap.put("errorClassName", errorClassName);
logger.info(httpStatus + "," + errorMessage, cause);
return Message.message(SystemStatusEnum.UN_SUCCEED.getStatue(), friendlyMessage, resultMap);
}
/**
* 将包装后的异常还原成实际异常类型
*
* @param request
* @return
*/
protected Throwable getCause(HttpServletRequest request) {
Throwable error = (Throwable) request.getAttribute("javax.servlet.error.exception");
if (error != null) {
while (error instanceof ServletException && error.getCause() != null) {
error = ((ServletException) error).getCause();
}
}
return error;
}
/**
* 这里指定友好提示信息
*
* @param cause
* @return
*/
protected String getFriendlyMessage(Throwable cause) {
// 你还可以通过异常类型判断需要提示的信息,具体的细化控制可指定实现
return "系统异常,请联系管理员";
}
}
附一个Spring Boot框架默认的异常处理类
/*
* Copyright 2012-2019 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.boot.autoconfigure.web.servlet.error;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* Basic global error {@link Controller}, rendering {@link ErrorAttributes}. More specific
* errors can be handled either using Spring MVC abstractions (e.g.
* {@code @ExceptionHandler}) or by adding servlet
* {@link AbstractServletWebServerFactory#setErrorPages server error pages}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Michael Stummvoll
* @author Stephane Nicoll
* @since 1.0.0
* @see ErrorAttributes
* @see ErrorProperties
*/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
/**
* Create a new {@link BasicErrorController} instance.
* @param errorAttributes the error attributes
* @param errorProperties configuration properties
*/
public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
this(errorAttributes, errorProperties, Collections.emptyList());
}
/**
* Create a new {@link BasicErrorController} instance.
* @param errorAttributes the error attributes
* @param errorProperties configuration properties
* @param errorViewResolvers error view resolvers
*/
public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties,
List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, errorViewResolvers);
Assert.notNull(errorProperties, "ErrorProperties must not be null");
this.errorProperties = errorProperties;
}
@Override
public String getErrorPath() {
return this.errorProperties.getPath();
}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
/**
* Determine if the stacktrace attribute should be included.
* @param request the source request
* @param produces the media type produced (or {@code MediaType.ALL})
* @return if the stacktrace attribute should be included
*/
protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
if (include == IncludeStacktrace.ALWAYS) {
return true;
}
if (include == IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}
/**
* Provide access to the error properties.
* @return the error properties
*/
protected ErrorProperties getErrorProperties() {
return this.errorProperties;
}
}