大型纪录片:学习若依源码(前后端分离版)之 " 异常处理"
前言
通常一个web框架中,有大量需要处理的异常。比如业务异常,权限不足等等。前端通过弹出提示信息的方式告诉用户出了什么错误。
通常情况下我们用try...catch...对异常进行捕捉处理,但是在实际项目中对业务模块进行异常捕捉,会造成代码重复和繁杂, 我们希望代码中只有业务相关的操作,所有的异常我们单独设立一个类来处理它。
全局异常就是对框架所有异常进行统一管理。若依前后端分离版本的异常处理是通过使用Spring的@RestControllerAdvice
注解来实现的。这个注解可以将一个类标记为全局异常处理器,用来捕获和处理所有的控制器层抛出的异常。
它中定义了一个 GlobalExceptionHandler
类,用来处理各种类型的异常,比如权限校验异常、请求方式不支持异常、业务异常、运行时异常、系统异常、自定义验证异常和演示模式异常。每种异常都有一个对应的@ExceptionHandler
注解的方法,用来返回一个AjaxResult对象,包含了错误码和错误信息。这样,前端就可以根据返回的结果来显示相应的提示或者处理逻辑。
所谓全局异常处理器就是使用@ControllerAdvice
注解。示例如下:
1、统一返回实体定义
cpp
package com.ruoyi.common.core.domain;
import java.util.HashMap;
/**
* 操作消息提醒
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L;
/**
* 返回错误消息
*
* @param code 错误码
* @param msg 内容
* @return 错误消息
*/
public static AjaxResult error(String msg)
{
AjaxResult json = new AjaxResult();
json.put("msg", msg);
json.put("code", 500);
return json;
}
/**
* 返回成功消息
*
* @param msg 内容
* @return 成功消息
*/
public static AjaxResult success(String msg)
{
AjaxResult json = new AjaxResult();
json.put("msg", msg);
json.put("code", 0);
return json;
}
}
2、定义登录异常定义
cpp
package com.ruoyi.common.exception;
/**
* 登录异常
*
* @author ruoyi
*/
public class LoginException extends RuntimeException
{
private static final long serialVersionUID = 1L;
protected final String message;
public LoginException(String message)
{
this.message = message;
}
@Override
public String getMessage()
{
return message;
}
}
3、基于@ControllerAdvice注解的Controller层的全局异常统一处理
cpp
package com.ruoyi.framework.web.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.LoginException;
/**
* 全局异常处理器
*
* @author ruoyi
*/
@RestControllerAdvice
public class GlobalExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 登录异常
*/
@ExceptionHandler(LoginException.class)
public AjaxResult loginException(LoginException e)
{
log.error(e.getMessage(), e);
return AjaxResult.error(e.getMessage());
}
}
@RestControllerAdvice
注解是一个组合注解,由@ControllerAdvice
和@ResponseBody
组成,而@ControllerAdvice
继承了@Component
,因此@RestControllerAdvice
本质上是个Component,适用于所有使用@RequestMapping
方法。
@ExceptionHandler
注解是用来指定异常处理方法的,当控制器中抛出指定类型的异常时,Spring MVC会自动调用使用@ExceptionHandler
标记的方法来处理异常。每种异常都有一个对应的@ExceptionHandler
注解的方法,是通过在@ExceptionHandler
注解中添加参数来实现的,参数是某个异常类的class,代表这个方法专门处理该类异常。
这样,当控制器中抛出不同类型的异常时,就会调用相应的@ExceptionHandler
注解的方法来处理。如果有多个方法处理同一类型的异常,那么会抛出Ambiguous @ExceptionHandler异常。
如果有多个类使用@RestControllerAdvice
注解,那么可以通过指定order属性来控制异常处理类的优先级,order值越小,优先级越高。
4、测试访问请求
cpp
@Controller
public class SysIndexController
{
/**
* 首页方法
*/
@GetMapping("/index")
public String index(ModelMap mmap)
{
/**
* 模拟用户未登录,抛出业务逻辑异常
*/
SysUser user = ShiroUtils.getSysUser();
if (StringUtils.isNull(user))
{
throw new LoginException("用户未登录,无法访问请求。");
}
mmap.put("user", user);
return "index";
}
}
根据上面代码含义,当我们未登录访问/index
时就会发生LoginException
业务逻辑异常,按照我们之前的全局异常配置以及统一返回实体实例化,访问后会出现AjaxResult格式JSON数据, 下面我们运行项目访问查看效果。
界面输出内容如下所示:
cpp
{
"msg": "用户未登录,无法访问请求。",
"code": 500
}
对于一些特殊情况,如接口需要返回json,页面请求返回html可以使用如下方法:
cpp
@ExceptionHandler(LoginException.class)
public Object loginException(HttpServletRequest request, LoginException e)
{
log.error(e.getMessage(), e);
if (ServletUtils.isAjaxRequest(request))
{
return AjaxResult.error(e.getMessage());
}
else
{
return new ModelAndView("/error/500");
}
}
若依系统的全局异常处理器为GlobalExceptionHandler
类
注意:如果全部异常处理返回json,那么可以使用@RestControllerAdvice
代替@ControllerAdvice
,这样在方法上就可以不需要添加@ResponseBody
。
结语
所以一定要养成看文档的好习惯啊。那么以上就是唐某的一些理解。这次的分享就到这里了。记得一键三连~( •̀ ω •́ )✧