SpringBoot实战项目学习(7)——进行统一的异常处理

文章目录

  • 项目结构
  • pom.xml
  • 配置文件application.yml
  • 页面模板error.html
  • 状态码封装类Status
  • 异常类
    • 异常基类BaseException
    • JSON异常类JsonException
    • 页面处理异常类PageException
  • 通用API接口封装类ApiResponse
  • 统一异常处理类DemoExceptionHandler
  • 测试控制器TestController
  • 测试

在Spring Boot中进行统一的异常处理,包括两种方式的处理:第一种对常见API形式的接口进行异常处理,统一封装返回格式;第二种是对模板页面请求的异常处理,统一处理错误页面

基于GitHub项目xkcoding/**spring-boot-demo**进行学习

项目地址:https://github.com/xkcoding/spring-boot-demo

项目结构

  • exception/handler包下一共有五个包:
    • constant包:保存状态码及对应信息
    • controller包:控制器
    • exception包:放置异常基类及其子类
    • handler包:放置统一异常处理(json和页面异常处理)
    • model

SpringBoot实战项目学习(7)——进行统一的异常处理_第1张图片

pom.xml

  • 页面需要使用thymeleaf模板引擎渲染,所以需要导入thymeleaf依赖

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>

	<artifactId>spring-boot-demo-exception-handlerartifactId>
	<version>1.0.0-SNAPSHOTversion>
	<packaging>jarpackaging>

	<name>spring-boot-demo-exception-handlername>
	<description>Demo project for Spring Bootdescription>

	<parent>
		<groupId>com.xkcodinggroupId>
		<artifactId>spring-boot-demoartifactId>
		<version>1.0.0-SNAPSHOTversion>
	parent>

	<properties>
		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
		<java.version>1.8java.version>
	properties>

	<dependencies>
    
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-thymeleafartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			<scope>testscope>
		dependency>

		<dependency>
			<groupId>org.projectlombokgroupId>
			<artifactId>lombokartifactId>
			<optional>trueoptional>
		dependency>
	dependencies>

	<build>
		<finalName>spring-boot-demo-exception-handlerfinalName>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

project>

配置文件application.yml

server:
  port: 8080
  servlet:
    context-path: /demo
spring:
  thymeleaf:
    cache: false      # 禁用thymeleaf模板缓存
    mode: HTML
    encoding: UTF-8
    servlet:
      content-type: text/html

页面模板error.html


<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
	<meta charset="UTF-8"/>
	<title>统一页面异常处理title>
head>
<body>
<h1>统一页面异常处理h1>
<div th:text="${message}">div>
body>
html>

状态码封装类Status

  • 这种通用类一般会放在common包下,在这里放在了constant包下
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; } }

异常类

异常基类BaseException

  • 异常基类需要继承运行时异常类RuntimeException
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; } }

JSON异常类JsonException

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

页面处理异常类PageException

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

通用API接口封装类ApiResponse

  • 通过这个类可以将各种异常、有状态的API或者自定义消息的API以统一的ApiResponse类型格式返回
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 <T extends BaseException> ApiResponse ofException(T t, Object data) { // 输出的类型T是BaseException的继承类 // 返回的ApiResponse类 return of(t.getCode(), t.getMessage(), data); } /** * 构造一个异常且带数据的API返回 * * @param t 异常 * @param {@link BaseException} 的子类 * @return ApiResponse */ public static <T extends BaseException> ApiResponse ofException(T t) { return ofException(t, null); } }

统一异常处理类DemoExceptionHandler

  • 该类将json异常返回为一个JSON字符串;
  • 将页面异常创建一个视图对象,并设置其相关属性,将视图返回到前端,由模板引擎进行解析并显示
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()); // 返回一个ApiResponse,本质上是一个JSON字符串,只有code和message,而data为null return ApiResponse.ofException(exception); } /** * 统一 页面 异常处理 * * @param exception PageException * @return 统一跳转到异常页面 */ @ExceptionHandler(value = PageException.class) public ModelAndView pageErrorHandler(PageException exception) { // 记录日志信息 log.error("【DemoPageException】:{}", exception.getMessage()); // 创建一个ModelAndView对象 ModelAndView view = new ModelAndView(); view.addObject("message", exception.getMessage()); // 设置视图的名字为error // 视图解析器会到templates文件夹下寻找同名的模板,并进行渲染(渲染message字段,将原始数据进行替换) view.setViewName(DEFAULT_ERROR_VIEW); return view; } }

测试控制器TestController

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

测试

  • 访问http://localhost:8080/demo/json,可以看到json类型的异常字符串

    在这里插入图片描述

  • 访问http://localhost:8080/demo/page,可以看到thymeleaf已经将相关字段渲染为异常信息

SpringBoot实战项目学习(7)——进行统一的异常处理_第2张图片

你可能感兴趣的:(SpringBoot实战项目,java,spring,boot)