1. 介绍
实际项目开发中,程序往往会发生各式各样的异常情况,特别是身为服务端开发人员的我们,总是不停的编写接口提供给前端调用,分工协作的情况下,避免不了异常的发生,如果直接将错误的信息直接暴露给用户,这样的体验可想而知,且对黑客而言,详细异常信息往往会提供非常大的帮助…所以我们需要集中的去处理这些异常,优化用户体验,隐藏异常信息。
1.1 目标
- 自定义异常
- 处理全局异常
3. 准备
我已经创建好了一个idea项目,不会创建的参考SpringBoot入门|第一篇。
创建项目的时候选择了 Spring Web
、 lombok
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.glitter.springboot</groupId>
<artifactId>replace4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>replace4</name>
<description>处理全局异常</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4. 开始
4.1 编写会报异常的Controller
TestController:
package com.glitter.springboot.replace4.logic.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @program:
* @author: Player
* @create: 2019-11-19
**/
@RestController
public class TestController {
@GetMapping("/test")
private String willThrowException(Integer num){
int i = 10 / num;
return "result:" + i;
}
}
启动项目访问http://localhost:8080/test?num=0会发现报错500
4.2 自定义异常
处理异常的方法有好几种,比如我们常用的 try-catch
的方式, try-catch
这种方式在必要情况下非常好使,但是在这里使用 try-catch
去处理就显得十分繁琐,在这里我们可以使用自定义异常去处理。
创建异常返回信息模板:
package com.glitter.springboot.replace4.exception.modal;
import lombok.Data;
import lombok.Generated;
/**
* @program:
* @author: Player
* @create: 2019-11-19
**/
@Data
public class ErrorResponseModal {
private int code;
private String message;
public ErrorResponseModal(int code, String message) {
this.code = code;
this.message = message;
}
}
创建自定义异常:
package com.glitter.springboot.replace4.exception.handle;
import lombok.Data;
/**
* @program:
* @author: Player
* @create: 2019-11-19
**/
@Data
public class CustomException extends RuntimeException {
private static final long serialVersionUID=1l;
private int code;
public CustomException() {
super();
}
public CustomException(int code, String message) {
super(message);
this.setCode(code);
}
}
更改controller:
package com.glitter.springboot.replace4.logic.controller;
import com.glitter.springboot.replace4.exception.handle.CustomException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @program:
* @author: Player
* @create: 2019-11-19
**/
@RestController
public class TestController {
@GetMapping("/test")
private String willThrowException(Integer num){
if(num==null||num==0){
throw new CustomException(400,"参数不能为空或0");
}
int i = 10 / num;
return "result:" + i;
}
}
在访问http://localhost:8080/test?num=就会发现异常信息已经变成了 参数不能为空或0
4.3 全局异常处理
注解概述:
@ControllerAdvice
捕获Controller
层抛出的异常,如果添加@ResponseBody
返回信息则为JSON
格式。@RestControllerAdvice
相当于@ControllerAdvice
与@ResponseBody
的结合体。@ExceptionHandler
统一处理一种类的异常,减少代码重复率,降低复杂度。
创建一个 GlobalExceptionHandler
类,并添加上 @RestControllerAdvice
注解就可以定义出异常通知类了,然后在定义的方法中添加上 @ExceptionHandler
即可实现异常的捕捉…
GlobalExceptionHandler.java
:
package com.glitter.springboot.replace4.exception.handle;
import com.glitter.springboot.replace4.exception.modal.ErrorResponseModal;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @program:
* @author: Player
* @create: 2019-11-19
**/
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
/**
* 定义要捕获的异常 可以多个 @ExceptionHandler({})
*
* @param request request
* @param e exception
* @param response response
* @return 响应结果
*/
@ExceptionHandler(CustomException.class)
public ErrorResponseModal customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
CustomException exception = (CustomException) e;
return new ErrorResponseModal(exception.getCode(), exception.getMessage());
}
/**
* 捕获 RuntimeException 异常
* TODO 如果你觉得在一个 exceptionHandler 通过 if (e instanceof xxxException) 太麻烦
* TODO 那么你还可以自己写多个不同的 exceptionHandler 处理不同异常
*
* @param request request
* @param e exception
* @param response response
* @return 响应结果
*/
@ExceptionHandler(RuntimeException.class)
public ErrorResponseModal runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
RuntimeException exception = (RuntimeException) e;
return new ErrorResponseModal(400, exception.getMessage());
}
/**
* 通用的接口映射异常处理方
*/
@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers,
HttpStatus status, WebRequest request) {
if (ex instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex;
return new ResponseEntity<>(new ErrorResponseModal(status.value(), exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status);
}
if (ex instanceof MethodArgumentTypeMismatchException) {
MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex;
logger.error("参数转换失败,方法:" + exception.getParameter().getMethod().getName() + ",参数:" + exception.getName()
+ ",信息:" + exception.getLocalizedMessage());
return new ResponseEntity<>(new ErrorResponseModal(status.value(), "参数转换失败"), status);
}
return new ResponseEntity<>(new ErrorResponseModal(status.value(), "参数转换失败"), status);
}
}
再访问http://localhost:8080/test?num=你就会发现异常信息已经变了