专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

spring Boot手把手教学(7): 抛弃try-catch, 如何优雅统一处理异常(包含404处理)

  • 1、前言
  • 2、自定义异常处理
  • 3、完整代码

1、前言

我们在项目开发中,难免碰到业务代码异常,无论是server 500, 还是其他异常。 我们这里简单说一下,如何抛弃try-catch,统一进行异常响应处理;

错误类型:

1、自定义业务服务代码异常(根据各自项目需求)

2、ServletException HTTP请求异常

3、内部代码异常:比如 mysql 查询表名错误

4、请求接口404:这个在统一异常中无法获取,需要额外处理

正常情况下,我们需要这么写:

JobController

 // 查询所有
    @GetMapping("/list")
    public Result getList() {
        Result result;
        try {
            List<Job> jobList = jobService.findAll();
            result = ResultGenerator.getSuccessResult(jobList);
        } catch (Exception e) {
            e.printStackTrace();
            result = ResultGenerator.getFailResult(e.getMessage());
        }
        return result;
    }

结果如图:

121_1.png

这样比较麻烦,要写很多try-catch.

那么有没有一种办法,帮我们自动拦截错误呢?

2、自定义异常处理

HandlerExceptionResolver, 就是处理异常的类;

源码:

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}

以上有四个参数其实相当于:request(请求)、response(响应)、hendler(处理器)、exception(异常);

接下来我们重写 WebMvcConfigurer.configureHandlerExceptionResolvers来处理异常:

com.scaffold.test.config.WebMvcConfig

package com.scaffold.test.config;

import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @Configuration public class WebMvcConfig implements WebMvcConfigurer { private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); // 统一异常处理 @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { exceptionResolvers.add((request, response, handler, e) -> { Result result = new Result(); // 错误处理 if (e instanceof ServiceException) { // 1、业务失败的异常,如“账号或密码错误” result.setCode(ResultCode.FAIL).setMessage(e.getMessage()); logger.info(e.getMessage()); } else if (e instanceof ServletException) { // 2、调用失败 result.setCode(ResultCode.FAIL).setMessage(e.getMessage()); } else { // 3、内部错误 result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员"); String message; if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s", request.getRequestURI(), handlerMethod.getBean().getClass().getName(), handlerMethod.getMethod().getName(), e.getMessage()); } else { message = e.getMessage(); } result.setMessage(message); logger.error(message, e); } responseResult(response, result); return new ModelAndView(); }); } // 处理返回数据格式 private void responseResult(HttpServletResponse response, Result result) { response.setCharacterEncoding("UTF-8"); response.setHeader("Content-type", "application/json;charset=UTF-8"); response.setStatus(200); try { response.getWriter().write(JSON.toJSONString(result)); } catch (IOException ex) { logger.error(ex.getMessage()); } } } 

1、业务失败的异常: ServiceException 服务异常类

package com.scaffold.test.base;

/**
 * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer
 */
public class ServiceException extends RuntimeException { public ServiceException() { } public ServiceException(String message) { super(message); } public ServiceException(String message, Throwable cause) { super(message, cause); } } 
// 部分代码
if (e instanceof ServiceException) {
  // 1、业务失败的异常,如“账号或密码错误”
  result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
  logger.info(e.getMessage());
} 

报错400代码;

2、调用失败: Servlet异常

if (e instanceof ServletException) {
 // 3、调用失败
 result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
}

结果如图:

121_2.png

3、内部代码异常:比如 mysql 查询表名错误

String message;
if (handler instanceof HandlerMethod) {
 HandlerMethod handlerMethod = (HandlerMethod) handler;
 message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
 request.getRequestURI(),
 handlerMethod.getBean().getClass().getName(), handlerMethod.getMethod().getName(), e.getMessage()); } else { message = e.getMessage(); } result.setMessage(message); logger.error(message, e); 

结果如图:

121_3.png

4、请求接口404:这个在统一异常中无法获取,需要额外处理

404进入不了统一异常处理,如下图:

121_4.png

所以,全局异常处理捕获不到404错误,需要专门写一个类用来处理404异常

com.scaffold.test.base.NotFoundException

package com.scaffold.test.base;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 @RestController public class NotFoundException implements ErrorController { private static final String ERROR_PATH = "/error"; @RequestMapping(ERROR_PATH) public Result error() { Result result = new Result(); // 2、接口不存在 result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在"); return result; } @Override public String getErrorPath() { return ERROR_PATH; } } 

121_5.png

3、完整代码

com.scaffold.test.base.Result: 统一返回实体类

package com.scaffold.test.base;

import lombok.Data;

/** * 统一API响应结果封装 */ @Data public class Result { private int code; private String message = "success"; private Object data; public Result setCode(ResultCode resultCode){ this.code = resultCode.code; return this; } public Result setMessage(String message){ this.message = message; return this; } public Result setData(Object data){ this.data = data; return this; } } 

com.scaffold.test.base.ResultCode: 统一返回响应码枚举,参考HTTP状态码

package com.scaffold.test.base;

/**
 * 响应码枚举,参考HTTP状态码的语义
 */
 public enum ResultCode { SUCCESS(200),//成功 FAIL(400),//失败 UNAUTHORIZED(401),//未认证(签名错误) NOT_FOUND(404),//接口不存在 INTERNAL_SERVER_ERROR(500);//服务器内部错误 public int code; ResultCode(int code) { this.code = code; } } 

统一异常拦截:com.scaffold.test.config.WebMvcConfig

package com.scaffold.test.config;

import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @Configuration public class WebMvcConfig implements WebMvcConfigurer { private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); // 统一异常处理 @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { exceptionResolvers.add((request, response, handler, e) -> { Result result = new Result(); // 异常处理 if (e instanceof ServiceException) { // 1、业务失败的异常,如“账号或密码错误” result.setCode(ResultCode.FAIL).setMessage(e.getMessage()); logger.info(e.getMessage()); }else if (e instanceof ServletException) { // 2、调用失败 result.setCode(ResultCode.FAIL).setMessage(e.getMessage()); } else { // 3、内部其他错误 result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员"); String message; if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s", request.getRequestURI(), handlerMethod.getBean().getClass().getName(), handlerMethod.getMethod().getName(), e.getMessage()); } else { message = e.getMessage(); } result.setMessage(message); logger.error(message, e); } responseResult(response, result); return new ModelAndView(); }); } // 处理响应数据格式 private void responseResult(HttpServletResponse response, Result result) { response.setCharacterEncoding("UTF-8"); response.setHeader("Content-type", "application/json;charset=UTF-8"); response.setStatus(200); try { response.getWriter().write(JSON.toJSONString(result)); } catch (IOException ex) { logger.error(ex.getMessage()); } } } 

404拦截处理:com.scaffold.test.base.NotFoundException

package com.scaffold.test.base;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 @RestController public class NotFoundException implements ErrorController { private static final String ERROR_PATH = "/error"; @RequestMapping(ERROR_PATH) public Result error() { Result result = new Result(); // 4、接口不存在 result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在"); return result; } @Override public String getErrorPath() { return ERROR_PATH; } } 

本文使用 tech.souyunku.com 排版

文章永久链接:https://tech.souyunku.com/34621

未经允许不得转载:搜云库技术团队 » spring Boot手把手教学(7): 抛弃try-catch, 如何优雅统一处理异常(包含404处理)

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们