diff --git a/spring-boot-demo-exception-handler/.gitignore b/spring-boot-demo-exception-handler/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/spring-boot-demo-exception-handler/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/spring-boot-demo-exception-handler/README.md b/spring-boot-demo-exception-handler/README.md new file mode 100644 index 0000000..3a51f35 --- /dev/null +++ b/spring-boot-demo-exception-handler/README.md @@ -0,0 +1,271 @@ +# spring-boot-demo-exception-handler + +> 此 demo 演示了如何在Spring Boot中进行统一的异常处理,包括了两种方式的处理:第一种对常见API形式的接口进行异常处理,统一封装返回格式;第二种是对模板页面请求的异常处理,统一处理错误页面。 + +## pom.xml + +```xml + + + 4.0.0 + + com.xkcoding + spring-boot-demo-exception-handler + 0.0.1-SNAPSHOT + jar + + spring-boot-demo-exception-handler + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.5.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.projectlombok + lombok + + + + + spring-boot-demo-exception-handler + + + org.springframework.boot + spring-boot-maven-plugin + + + + + +``` + +## ApiResponse.java + +> 统一的API格式返回封装,里面涉及到的 `BaseException` 和`Status` 这两个类,具体代码见 demo。 + +```java +/** + *

+ * 通用的 API 接口封装 + *

+ * + * @package: com.xkcoding.exception.handler.model + * @description: 通用的 API 接口封装 + * @author: yangkai.shen + * @date: Created in 2018/10/2 8:57 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Data +public class ApiResponse { + /** + * 状态码 + */ + private Integer code; + + /** + * 返回内容 + */ + private String message; + + /** + * 返回数据 + */ + private Object data; + + /** + * 无参构造函数 + */ + private ApiResponse() { + + } + + /** + * 全参构造函数 + * + * @param code 状态码 + * @param message 返回内容 + * @param data 返回数据 + */ + private ApiResponse(Integer code, String message, Object data) { + this.code = code; + this.message = message; + this.data = data; + } + + /** + * 构造一个自定义的API返回 + * + * @param code 状态码 + * @param message 返回内容 + * @param data 返回数据 + * @return ApiResponse + */ + public static ApiResponse of(Integer code, String message, Object data) { + return new ApiResponse(code, message, data); + } + + /** + * 构造一个成功且带数据的API返回 + * + * @param data 返回数据 + * @return ApiResponse + */ + public static ApiResponse ofSuccess(Object data) { + return ofStatus(Status.OK, data); + } + + /** + * 构造一个成功且自定义消息的API返回 + * + * @param message 返回内容 + * @return ApiResponse + */ + public static ApiResponse ofMessage(String message) { + return of(Status.OK.getCode(), message, null); + } + + /** + * 构造一个有状态的API返回 + * + * @param status 状态 {@link Status} + * @return ApiResponse + */ + public static ApiResponse ofStatus(Status status) { + return ofStatus(status, null); + } + + /** + * 构造一个有状态且带数据的API返回 + * + * @param status 状态 {@link Status} + * @param data 返回数据 + * @return ApiResponse + */ + public static ApiResponse ofStatus(Status status, Object data) { + return of(status.getCode(), status.getMessage(), data); + } + + /** + * 构造一个异常且带数据的API返回 + * + * @param t 异常 + * @param data 返回数据 + * @param {@link BaseException} 的子类 + * @return ApiResponse + */ + public static ApiResponse ofException(T t, Object data) { + return of(t.getCode(), t.getMessage(), data); + } + + /** + * 构造一个异常且带数据的API返回 + * + * @param t 异常 + * @param {@link BaseException} 的子类 + * @return ApiResponse + */ + public static ApiResponse ofException(T t) { + return ofException(t, null); + } +} +``` + +## DemoExceptionHandler.java + +```java +/** + *

+ * 统一异常处理 + *

+ * + * @package: com.xkcoding.exception.handler.handler + * @description: 统一异常处理 + * @author: yangkai.shen + * @date: Created in 2018/10/2 9:26 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@ControllerAdvice +@Slf4j +public class DemoExceptionHandler { + private static final String DEFAULT_ERROR_VIEW = "error"; + + /** + * 统一 json 异常处理 + * + * @param exception JsonException + * @return 统一返回 json 格式 + */ + @ExceptionHandler(value = JsonException.class) + @ResponseBody + public ApiResponse jsonErrorHandler(JsonException exception) { + log.error("【JsonException】:{}", exception.getMessage()); + return ApiResponse.ofException(exception); + } + + /** + * 统一 页面 异常处理 + * + * @param exception PageException + * @return 统一跳转到异常页面 + */ + @ExceptionHandler(value = PageException.class) + public ModelAndView pageErrorHandler(PageException exception) { + log.error("【DemoPageException】:{}", exception.getMessage()); + ModelAndView view = new ModelAndView(); + view.addObject("message", exception.getMessage()); + view.setViewName(DEFAULT_ERROR_VIEW); + return view; + } +} +``` + +## error.html + +> 位于 `src/main/resources/template` 目录下 + +```html + + + + + 统一页面异常处理 + + +

统一页面异常处理

+
+ + +``` + diff --git a/spring-boot-demo-exception-handler/pom.xml b/spring-boot-demo-exception-handler/pom.xml new file mode 100644 index 0000000..8b4cc40 --- /dev/null +++ b/spring-boot-demo-exception-handler/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + com.xkcoding + spring-boot-demo-exception-handler + 0.0.1-SNAPSHOT + jar + + spring-boot-demo-exception-handler + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.5.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.projectlombok + lombok + + + + + spring-boot-demo-exception-handler + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/SpringBootDemoExceptionHandlerApplication.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/SpringBootDemoExceptionHandlerApplication.java new file mode 100644 index 0000000..ddab733 --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/SpringBootDemoExceptionHandlerApplication.java @@ -0,0 +1,25 @@ +package com.xkcoding.exception.handler; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + *

+ * 启动类 + *

+ * + * @package: com.xkcoding.exception.handler + * @description: 启动类 + * @author: yangkai.shen + * @date: Created in 2018/10/2 8:49 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@SpringBootApplication +public class SpringBootDemoExceptionHandlerApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootDemoExceptionHandlerApplication.class, args); + } +} diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/constant/Status.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/constant/Status.java new file mode 100644 index 0000000..da60aac --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/constant/Status.java @@ -0,0 +1,42 @@ +package com.xkcoding.exception.handler.constant; + +import lombok.Getter; + +/** + *

+ * 状态码封装 + *

+ * + * @package: com.xkcoding.exception.handler.constant + * @description: 状态码封装 + * @author: yangkai.shen + * @date: Created in 2018/10/2 9:02 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Getter +public enum Status { + /** + * 操作成功 + */ + OK(200, "操作成功"), + + /** + * 未知异常 + */ + UNKNOWN_ERROR(500, "服务器出错啦"); + /** + * 状态码 + */ + private Integer code; + /** + * 内容 + */ + private String message; + + Status(Integer code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/controller/TestController.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/controller/TestController.java new file mode 100644 index 0000000..48dd975 --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/controller/TestController.java @@ -0,0 +1,38 @@ +package com.xkcoding.exception.handler.controller; + +import com.xkcoding.exception.handler.constant.Status; +import com.xkcoding.exception.handler.exception.JsonException; +import com.xkcoding.exception.handler.exception.PageException; +import com.xkcoding.exception.handler.model.ApiResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +/** + *

+ * 测试Controller + *

+ * + * @package: com.xkcoding.exception.handler.controller + * @description: 测试Controller + * @author: yangkai.shen + * @date: Created in 2018/10/2 8:49 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Controller +public class TestController { + + @GetMapping("/json") + @ResponseBody + public ApiResponse jsonException() { + throw new JsonException(Status.UNKNOWN_ERROR); + } + + @GetMapping("/page") + public ModelAndView pageException() { + throw new PageException(Status.UNKNOWN_ERROR); + } +} diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/BaseException.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/BaseException.java new file mode 100644 index 0000000..d4e037d --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/BaseException.java @@ -0,0 +1,37 @@ +package com.xkcoding.exception.handler.exception; + +import com.xkcoding.exception.handler.constant.Status; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 异常基类 + *

+ * + * @package: com.xkcoding.exception.handler.exception + * @description: 异常基类 + * @author: yangkai.shen + * @date: Created in 2018/10/2 9:31 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class BaseException extends RuntimeException { + private Integer code; + private String message; + + public BaseException(Status status) { + super(status.getMessage()); + this.code = status.getCode(); + this.message = status.getMessage(); + } + + public BaseException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } +} diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/JsonException.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/JsonException.java new file mode 100644 index 0000000..b72ddef --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/JsonException.java @@ -0,0 +1,29 @@ +package com.xkcoding.exception.handler.exception; + +import com.xkcoding.exception.handler.constant.Status; +import lombok.Getter; + +/** + *

+ * JSON异常 + *

+ * + * @package: com.xkcoding.exception.handler.exception + * @description: JSON异常 + * @author: yangkai.shen + * @date: Created in 2018/10/2 9:18 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Getter +public class JsonException extends BaseException { + + public JsonException(Status status) { + super(status); + } + + public JsonException(Integer code, String message) { + super(code, message); + } +} diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/PageException.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/PageException.java new file mode 100644 index 0000000..102327a --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/exception/PageException.java @@ -0,0 +1,29 @@ +package com.xkcoding.exception.handler.exception; + +import com.xkcoding.exception.handler.constant.Status; +import lombok.Getter; + +/** + *

+ * 页面异常 + *

+ * + * @package: com.xkcoding.exception.handler.exception + * @description: 页面异常 + * @author: yangkai.shen + * @date: Created in 2018/10/2 9:18 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Getter +public class PageException extends BaseException { + + public PageException(Status status) { + super(status); + } + + public PageException(Integer code, String message) { + super(code, message); + } +} diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/handler/DemoExceptionHandler.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/handler/DemoExceptionHandler.java new file mode 100644 index 0000000..191bd69 --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/handler/DemoExceptionHandler.java @@ -0,0 +1,57 @@ +package com.xkcoding.exception.handler.handler; + +import com.xkcoding.exception.handler.exception.JsonException; +import com.xkcoding.exception.handler.exception.PageException; +import com.xkcoding.exception.handler.model.ApiResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +/** + *

+ * 统一异常处理 + *

+ * + * @package: com.xkcoding.exception.handler.handler + * @description: 统一异常处理 + * @author: yangkai.shen + * @date: Created in 2018/10/2 9:26 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@ControllerAdvice +@Slf4j +public class DemoExceptionHandler { + private static final String DEFAULT_ERROR_VIEW = "error"; + + /** + * 统一 json 异常处理 + * + * @param exception JsonException + * @return 统一返回 json 格式 + */ + @ExceptionHandler(value = JsonException.class) + @ResponseBody + public ApiResponse jsonErrorHandler(JsonException exception) { + log.error("【JsonException】:{}", exception.getMessage()); + return ApiResponse.ofException(exception); + } + + /** + * 统一 页面 异常处理 + * + * @param exception PageException + * @return 统一跳转到异常页面 + */ + @ExceptionHandler(value = PageException.class) + public ModelAndView pageErrorHandler(PageException exception) { + log.error("【DemoPageException】:{}", exception.getMessage()); + ModelAndView view = new ModelAndView(); + view.addObject("message", exception.getMessage()); + view.setViewName(DEFAULT_ERROR_VIEW); + return view; + } +} diff --git a/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/model/ApiResponse.java b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/model/ApiResponse.java new file mode 100644 index 0000000..4731ce8 --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/java/com/xkcoding/exception/handler/model/ApiResponse.java @@ -0,0 +1,132 @@ +package com.xkcoding.exception.handler.model; + +import com.xkcoding.exception.handler.constant.Status; +import com.xkcoding.exception.handler.exception.BaseException; +import lombok.Data; + +/** + *

+ * 通用的 API 接口封装 + *

+ * + * @package: com.xkcoding.exception.handler.model + * @description: 通用的 API 接口封装 + * @author: yangkai.shen + * @date: Created in 2018/10/2 8:57 PM + * @copyright: Copyright (c) 2018 + * @version: V1.0 + * @modified: yangkai.shen + */ +@Data +public class ApiResponse { + /** + * 状态码 + */ + private Integer code; + + /** + * 返回内容 + */ + private String message; + + /** + * 返回数据 + */ + private Object data; + + /** + * 无参构造函数 + */ + private ApiResponse() { + + } + + /** + * 全参构造函数 + * + * @param code 状态码 + * @param message 返回内容 + * @param data 返回数据 + */ + private ApiResponse(Integer code, String message, Object data) { + this.code = code; + this.message = message; + this.data = data; + } + + /** + * 构造一个自定义的API返回 + * + * @param code 状态码 + * @param message 返回内容 + * @param data 返回数据 + * @return ApiResponse + */ + public static ApiResponse of(Integer code, String message, Object data) { + return new ApiResponse(code, message, data); + } + + /** + * 构造一个成功且带数据的API返回 + * + * @param data 返回数据 + * @return ApiResponse + */ + public static ApiResponse ofSuccess(Object data) { + return ofStatus(Status.OK, data); + } + + /** + * 构造一个成功且自定义消息的API返回 + * + * @param message 返回内容 + * @return ApiResponse + */ + public static ApiResponse ofMessage(String message) { + return of(Status.OK.getCode(), message, null); + } + + /** + * 构造一个有状态的API返回 + * + * @param status 状态 {@link Status} + * @return ApiResponse + */ + public static ApiResponse ofStatus(Status status) { + return ofStatus(status, null); + } + + /** + * 构造一个有状态且带数据的API返回 + * + * @param status 状态 {@link Status} + * @param data 返回数据 + * @return ApiResponse + */ + public static ApiResponse ofStatus(Status status, Object data) { + return of(status.getCode(), status.getMessage(), data); + } + + /** + * 构造一个异常且带数据的API返回 + * + * @param t 异常 + * @param data 返回数据 + * @param {@link BaseException} 的子类 + * @return ApiResponse + */ + public static ApiResponse ofException(T t, Object data) { + return of(t.getCode(), t.getMessage(), data); + } + + /** + * 构造一个异常且带数据的API返回 + * + * @param t 异常 + * @param {@link BaseException} 的子类 + * @return ApiResponse + */ + public static ApiResponse ofException(T t) { + return ofException(t, null); + } +} diff --git a/spring-boot-demo-exception-handler/src/main/resources/application.yml b/spring-boot-demo-exception-handler/src/main/resources/application.yml new file mode 100644 index 0000000..a383b0b --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/resources/application.yml @@ -0,0 +1,11 @@ +server: + port: 8080 + servlet: + context-path: /demo +spring: + thymeleaf: + cache: false + mode: HTML + encoding: UTF-8 + servlet: + content-type: text/html \ No newline at end of file diff --git a/spring-boot-demo-exception-handler/src/main/resources/templates/error.html b/spring-boot-demo-exception-handler/src/main/resources/templates/error.html new file mode 100644 index 0000000..0b8c802 --- /dev/null +++ b/spring-boot-demo-exception-handler/src/main/resources/templates/error.html @@ -0,0 +1,11 @@ + + + + + 统一页面异常处理 + + +

统一页面异常处理

+
+ + \ No newline at end of file diff --git a/spring-boot-demo-exception-handler/src/test/java/com/xkcoding/exception/handler/SpringBootDemoExceptionHandlerApplicationTests.java b/spring-boot-demo-exception-handler/src/test/java/com/xkcoding/exception/handler/SpringBootDemoExceptionHandlerApplicationTests.java new file mode 100644 index 0000000..399902c --- /dev/null +++ b/spring-boot-demo-exception-handler/src/test/java/com/xkcoding/exception/handler/SpringBootDemoExceptionHandlerApplicationTests.java @@ -0,0 +1,16 @@ +package com.xkcoding.exception.handler; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringBootDemoExceptionHandlerApplicationTests { + + @Test + public void contextLoads() { + } + +}