SpringBoot初始教程之统一异常处理详解

1.介绍

在日常开发中发生了异常,往往是需要通过一个统一的异常处理处理所有异常,来保证客户端能够收到友好的提示。SpringBoot在页面发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理,当然也可以自定义这个路径

application.yaml

server:

port: 8080

error:

path: /custom/error

BasicErrorController提供两种返回错误一种是页面返回、当你是页面请求的时候就会返回页面,另外一种是json请求的时候就会返回json错误

@RequestMapping(produces = "text/html")

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 ? new ModelAndView("error", model) : modelAndView);

}

@RequestMapping

@ResponseBody

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<Map<String, Object>>(body, status);

}

分别会有如下两种返回

{

"timestamp": 1478571808052,

"status": 404,

"error": "Not Found",

"message": "No message available",

"path": "/rpc"

}

2.通用Exception处理

通过使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常

下面针对两种异常进行了特殊处理分别返回页面和json数据,使用这种方式有个局限,无法根据不同的头部返回不同的数据格式,而且无法针对404、403等多种状态进行处理

@ControllerAdvice

public class GlobalExceptionHandler {

public static final String DEFAULT_ERROR_VIEW = "error";

@ExceptionHandler(value = CustomException.class)

@ResponseBody

public ResponseEntity defaultErrorHandler(HttpServletRequest req, CustomException e) throws Exception {

return ResponseEntity.ok("ok");

}

@ExceptionHandler(value = Exception.class)

public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {

ModelAndView mav = new ModelAndView();

mav.addObject("exception", e);

mav.addObject("url", req.getRequestURL());

mav.setViewName(DEFAULT_ERROR_VIEW);

return mav;

}

}

3.自定义BasicErrorController 错误处理

在初始介绍哪里提到了BasicErrorController,这个是SpringBoot的默认错误处理,也是一种全局处理方式。咱们可以模仿这种处理方式自定义自己的全局错误处理

下面定义了一个自己的BasicErrorController,可以根据自己的需求自定义errorHtml()和error()的返回值。

@Controller

@RequestMapping("${server.error.path:${error.path:/error}}")

public class BasicErrorController extends AbstractErrorController {

private final ErrorProperties errorProperties;

private static final Logger LOGGER = LoggerFactory.getLogger(BasicErrorController.class);

@Autowired

private ApplicationContext applicationContext;

/**

* Create a new {@link org.springframework.boot.autoconfigure.web.BasicErrorController} instance.

*

* @param errorAttributes the error attributes

* @param errorProperties configuration properties

*/

public BasicErrorController(ErrorAttributes errorAttributes,

ErrorProperties errorProperties) {

this(errorAttributes, errorProperties,

Collections.<ErrorViewResolver>emptyList());

}

/**

* Create a new {@link org.springframework.boot.autoconfigure.web.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 = "text/html")

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);

insertError(request);

return modelAndView == null ? new ModelAndView("error", model) : modelAndView;

}

@RequestMapping

@ResponseBody

public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {

Map<String, Object> body = getErrorAttributes(request,

isIncludeStackTrace(request, MediaType.ALL));

HttpStatus status = getStatus(request);

insertError(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) {

ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();

if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {

return true;

}

if (include == ErrorProperties.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;

}

}

SpringBoot提供了一种特殊的Bean定义方式,可以让我们容易的覆盖已经定义好的Controller,原生的BasicErrorController是定义在ErrorMvcAutoConfiguration中的

具体代码如下:

@Bean

@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)

public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {

return new BasicErrorController(errorAttributes, this.serverProperties.getError(),

this.errorViewResolvers);

}

可以看到这个注解@ConditionalOnMissingBean 意思就是定义这个bean 当 ErrorController.class 这个没有定义的时候, 意思就是说只要我们在代码里面定义了自己的ErrorController.class时,这段代码就不生效了,具体自定义如下:

@Configuration

@ConditionalOnWebApplication

@ConditionalOnClass({Servlet.class, DispatcherServlet.class})

@AutoConfigureBefore(WebMvcAutoConfiguration.class)

@EnableConfigurationProperties(ResourceProperties.class)

public class ConfigSpringboot {

@Autowired(required = false)

private List<ErrorViewResolver> errorViewResolvers;

private final ServerProperties serverProperties;

public ConfigSpringboot(

ServerProperties serverProperties) {

this.serverProperties = serverProperties;

}

@Bean

public MyBasicErrorController basicErrorController(ErrorAttributes errorAttributes) {

return new MyBasicErrorController(errorAttributes, this.serverProperties.getError(),

this.errorViewResolvers);

}

}

在使用的时候需要注意MyBasicErrorController不能被自定义扫描Controller扫描到,否则无法启动。

3.总结

一般来说自定义BasicErrorController这种方式比较实用,因为可以通过不同的头部返回不同的数据格式,在配置上稍微复杂一些,但是从实用的角度来说比较方便而且可以定义通用组件

本文代码:SpringBoot-Learn_jb51.rar

以上是 SpringBoot初始教程之统一异常处理详解 的全部内容, 来源链接: utcz.com/p/213248.html

回到顶部