JavaWeb使用springmvc进行后端全局异常处理

全局异常处理的目的:
在后端代码校验中,凡是出现用户因前端操作不当而导致的问题,都需要将错误信息返回给前端,供用户查看。
于是,我们在后台,将所有的错误声明成异常,通过创建不同类型的异常,对应不同类型的错误,以此通过我们手动抛出异常,来让springmvc提供的全局异常捕获器来捕获到,并自动将错误信息为我们响应到前端页面。

全局异常处理的步骤
第一步:创建一个新类型的异常
这个异常需要继承RuntimeException类。
需要重载这个新的异常类的构造方法,创建一个带参的构造方法,参数为String类型的message
并在重载的构造方法中,调用父类的有参构造方法。
代码举例:
我们以用户登录的场景为例,创建一个专门用于抛出用户登录时输入信息是否合法的异常类,名为:LoginException,这个自定义的类,是一个在代码运行过程中,才会触发的异常,因为只有在代码运行过程中,用户的请求才能被我们的代码所处理。所以,我们的这个登录异常类,需要继承运行时异常的类RuntimeException。
// 对应上面的 1
public class LoginException extends RuntimeException {

    //这个ID是继承RuntimeException方法,要实现的,对于我们逻辑业务没有影响
    //如果不写会报警告。
  
    private static final long serialVersionUID = 1L;
    
    // 对应上面的 2
    public LoginException(String message) {
        //对应上面的 3
        super(message);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
这里再详细的刨析一下,我们的代码,在底层是如果调用的。上面我们继承了RuntimeException类,这个类中重载了多个构造方法,其目的就是根据不同的参数列表来接收不同情况下的参数,我们现在的业务需求,仅需要向前端发送错误信息即可,没有其他的需求。所以,我们重载的方法对应的是父类RuntimeException类中的参数列表为String message的构造方法。我们传入的message会传递给父类,被父类拿到。具体代码:

    public RuntimeException(String message) {
        super(message);
    }

1
2
3
4
而在这个类中,可以看到,父类RuntimeException又继续调用了它的父类,为Exception,将我们传递给它的message值又传递给了Exception类中的参数列表为String message的构造方法。具体代码:

    public Exception(String message) {
        super(message);
    }
1
2
3
而Exception类会继续调用它的父类(Exeption又继承了Throwable类),所以,它继续调用Throwable的参数列表为String message的构造方法,将这一路传来的message继续向上传。具体代码:

    public Throwable(String message) {
        fillInStackTrace();
        detailMessage = message;
    }
1
2
3
4
最终,message被赋值给了detailMessage这个实例变量,以备使用。
【在Throwable类中接下去的底层操作,不在本节展开,后续会专门书写文章介绍】

第二步:创建一个全部异常捕获处理器
用于捕获所有代码抛出的异常,并在次处理器中给出解决方案。
这里说是一个处理器,其实就是一个Java类,只不过我们在这里,运用注解的方式,给它赋予神圣的权限,让它从一个普通的Java类,变成一个能够捕获全局异常并给出处理的核心关键类
我们需要做什么呢?

在这个类上加上@ControllerAdivce注解,来表明它的身份
继续以我们登录的需求为例,我们在这里创建一个名为GlobalExceptionHandler,只所以起这么长的名字,其实完全是为了让它更具有表义性,翻译过来叫做:全局异常处理器。这个的类名,随便怎么叫都可以。

    @ControllerAdvice
    public class GlobalExceptionHandler {
        
    }
1
2
3
4
第三步:在这个全局异常捕获处理器中,声明我们需要捕获的异常类型及具体解决方案
既然我们创建好了全局异常处理器,那么现在就来的新的问题,作为异常处理的核心关键,它都需要做些什么?
我们现在来思考一下,既然是一个全局异常捕获处理器,它就需要①捕获全局的异常,也就是一旦其他地方的代码抛出了异常,它可以进行捕获。相当于一个警察,看到一个正在偷自行车的小偷,他可以直接上去抓住他,对他进行一系列的处理。那么这个前去抓小偷的过程,就是捕获异常的过程。②捕获到异常后,需要对异常进行处理。至于怎么处理,这就完全根据我们自己的需求来了,比如我们此时的需求就是向前端页面发送回去错误信息即可。这里相当于,警察抓住小偷后,可以对他进行思想教育、或者是直接上铐子带回所里,等候发落。
这里可能有同学会想到,那么一旦出现异常,我们都必须去捕获吗?其实并不是的,我们可以专门指定我们需要捕获的异常类型。这就好比警察也有很多种,有交警、刑警、武警等等,分别对不用的异常情况进行处理,对于交警来说,他更多的是专注于交通类的犯罪情况,而刑警专注于刑事犯罪一类的情况。

言归正传:我们需要做什么?
在这个类种,创建一个方法,这个方法里写上我们具体如何处理这个异常。通过在方法上,添加@ExceptionHandler(LoginException.class)注解,传入一个我们想要捕获的异常类型。例如,我们的需求是捕获登陆时出现的异常,也就是登陆时一旦出现了错误,我们就抛出一个异常,比如:这个异常可能是由于用户名或者密码错误,抑或是验证码输入有误等等。
这里@ExceptionHandler()注解是springmvc规定的,我们直接使用即可。那在这个注解中,我们传入了一个参数,这个参数由又我们自己指定的,它表示具体的异常的类型。由于刚才,我们在第一步中,专门创建了一个登录时的异常,LoginException,所以,我在这里把这个异常的类对象传给了它作为参数。一定要注意,是 .class 。
接下来,就是对异常进行具体的处理,也就是我们要给出的解决方案。
在完成我们需求案例之前,在这里,罗列出所有异常处理的类型,可以供大家使用。
在后端,对于错误异常的处理,无非就以下几种情况:
①处理完后,需要发送数据到前端页面给用户显示。
请大家思考一下,我们现在的需求,是否就是如此呢?稍后公布答案。
这里需要特别强调,如果后端要发送数据给前端,那通过的方式就是传输JSON类型的字符串。而如果一旦后端要往前端发送数据(如JSON字符串),则前端一定得有方法去接收我们传过去的数据。通常情况下,是前端发送异步请求到后端,无论是通过调用后端接口还是直接访问指定的url路径,我们在后台直接将值返回给前端异步请求的方法,异步请求的方法中会有专门的回调函数等待着我们返回到前端的值
如果前端没有专门接收传回数据的方法,那么数据就会脱离页面而单独存在,仅仅就是一个字符串。

②不需要发送任何数据,直接跳转页面。
比如,在开发中,一旦用户做出了某些不合法的操作,我们就可以强制跳转页 面,让用户到达我们指定的页面进行访问。

③不仅需要传输数据,又需要跳转页面。
这种情况,多用于前端发送的是同步请求,通过同步请求,后端拿取到传来的参数值,并作出页面跳转的响应,同时还可以将数据封装带回前端。

那么我们的需求是哪种情况呢?
这其实取决于我们前端发送的请求类型,如果是同步请求,那么我们没有办法通过直接返回数据的方式为前端传输数据,因为即使我们可以传输,可是前端页面没有任何的异步回调函数等待着我们的响应。所以,需要用③的方法。
如果是异步请求,那这就相当于,前端通过了异步请求方法来调用我们后端的数据,那肯定是有回调函数可以接收的,那我们就可以使用①的方法。

这里为大家同时列举同步请求和异步请求的两种情况,大家可以在学会后,灵活运用。
同步请求
我们假设,用户登录的时候,填写的是一个form表单,里面有用户名、密码、验证码三项内容。此时,用户一旦点击提交按钮,表单将直接以同步请求的方式,传输到后台的指定controller中。那我们就需要想到,此时我们无法在全局异常处理器中将错误直接返回,而是通过重新跳转回登陆页面,并将错误信息一并返回。
这种需求,在SpringMVC中,我们可以直接使用ModelAndView对象进行处理。直接上代码:

    @ExceptionHandler(LoginException.class)
    @ResponseBody
    public ModelAndView handle(LoginException e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error",e.getMessage());
        mv.setViewName("login");
        return mv;
    }
1
2
3
4
5
6
7
8
解释一下:
@ResponseBody 标签表示,返回结果不跳转页面。
mv.addObject(“error”,e.getMessage()) 是将错误数据封装成一个key=value的形式,Key为error,Value为我们传给它的message错误信息。
mv.setViewName() 是我们指定的跳转页面的名字,这里由于我设置了视图解析器的前后缀配置。只写了一个login,最终其实返回的页面是user/login.html(这个视个人配置而定)

如果是异步请求

    @ExceptionHandler(LoginException.class)
    @ResponseBody
    public Map<String, String> handle(LoginException e){
        Map<String, String> result = new HashMap<>();
        result.put("error",e.getMessage());
        return result;
    }
1
2
3
4
5
6
7
我们可以直接创建一个Map集合,还是通过key=value的格式。将Key指定为error,将value指定为我们传给它的message。然后返回这个map集合即可。要注意,还是需要添加@ResponseBody这个注解,来表明不跳转任何页面。而是,直接以JSON格式的字符串输出内容到前端页面。
那此时,前端的回调函数就可以拿到我们这里传过去的数据,其中有key为error,value为message的值。

第四步:在具体的业务代码中,抛出异常
既然我们已经定义好了如何处理异常,那么现在就需要我们在用户输入信息有误的时候,直接手动抛出异常

例如:我们在接收到前端用户传来的数据后,将数据进行核查。无论是与数据库进行交互,获得用户名和密码;还是调用第三方验证码接口来获得正确的验证码。我们最终肯定会得出结论,用户的输入是否是有错的。我们现在来讨论用错的情况。如果,用户的用户名和密码不匹配,那么我们直接在代码中,书写以下代码:

if(用户名或密码错误){
    throw new LoginException("用户名或密码输入错误");
}
1
2
3
通过我们自己手动抛出异样,也就是自己new一个自己定义的LoginException类型的异常,并将错误信息作为参数传给这个构造方法,以此来获得一个异常对象。一旦这里抛出了异常,由于第二、三步中,我们已经声明了全局异常处理器捕获的异常类型,这里肯定可以捕获到我们抛出的异常,此时,我们的异常将被处理器拿到,同时进入到我们指定的处理方法中(也就是会直接返回到前端页面、或者跳转新页面)。而这句代码接下的代码将不会再执行。

验证码错误的话,代码也是一样的,只是错误信息不同:

if(验证码错误){
    throw new LoginException("验证码输入错误");
}
1
2
3
那如果用户名和密码正确、同时验证码也正确,那么就不会进入到if语句中,也就不会抛出异常,则可以继续进行接下面的代码,比如:登陆成功后,跳转到首页,或者其他操作。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值