第7章—SpringMVC高级技术—处理异常

简介: 处理异常处理异常不管发生什么事情,不管是好的还是坏的,Servlet请求的输出都是一个Servlet响应。如果在请求处理的时候,出现了异常,那它的输出依然会是Servlet响应。

处理异常

处理异常

不管发生什么事情,不管是好的还是坏的,Servlet请求的输出都是一个Servlet响应。如果在请求处理的时候,出现了异常,那它的输出依然会是Servlet响应。异常必须要以某种方式转换为响应。

Spring提供了多种方式将异常转换为响应:

特定的Spring异常将会自动映射为指定的HTTP状态码;
  异常上可以添加@ResponseStatus注解,从而将其映射为某一个HTTP状态码;

在方法上可以添加@ExceptionHandler注解,使其用来处理异常。

3.1将异常映射为HTTP状态码

在默认情况下,Spring会将自身的一些异常自动转换为合适的状态码。

Spring的一些异常会默认映射为HTTP状态码

Spring异常 HTTP状态码
BindException 400 - Bad Request
ConversionNotSupportedException 500 - Internal Server Error
HttpMediaTypeNotAcceptableException 406 - Not Acceptable
HttpMediaTypeNotSupportedException 415 - Unsupported Media Type
HttpMessageNotReadableException 400 - Bad Request
MissingServletRequestParameterException 400 - Bad Request
MissingServletRequestPartException 400 - Bad Request
NoSuchRequestHandlingMethodException 404 - Not Found
TypeMismatchException 400 - Bad Request
HttpMessageNotWritableException 500 - Internal Server Error
HttpRequestMethodNotSupportedException 405 - Method Not Allowed

异常一般会由Spring自身抛出,作为DispatcherServlet处理过程中或执行校验时出现问题的结果。

如果DispatcherServlet无法找到适合处理请求的控制器方法,那么将会抛出NoSuchRequestHandlingMethodException异常,最终的结果就是产生404状态码的响应(Not Found)。

3.2@ResponseStatus注解

Spring提供了一种机制,能够通过@ResponseStatus注解将异常映射为HTTP状态码。

     public String spittle(@PathVariable("spittleId") long spittledId ,Model  model){
         Spittle spittle = sipttleRepository.findOne(spittleId);
         if(spittle == null){
             throw new SpittleNotFoundException();
         }
         model.addAttribute(spittle);
         return "spittle";
     }

通过ID检索Spittle对象。如果findOne()方法能够返回Spittle对象的话,那么会将Spittle放到模型中,然后名为spittle的视图会负责将其渲染到响应之中。但是如果findOne()方法返回null的话,那么将会抛出SpittleNotFoundException异常。

public class SpittleNotFoundException extends RuntimeException {

}

如果调用spittle()方法来处理请求,并且给定ID获取到的结果为空,那么SpittleNotFoundException(默认)将会产生500状态码(Internal Server Error)的响应。实际上,如果出现任何没有映射的异常,响应都会带有500状态码,故返回的不精确,可以修改。

使用@ResponseStatus注解将SpittleNotFoundException映射为HTTP状态码404。

@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="Spittle Not Found")
public class SpittleNotFoundException extends RuntimeException {

}

在引入@ResponseStatus注解之后,如果控制器方法抛出SpittleNotFoundException异常的话,响应将会具有404状态码,这是因为Spittle Not Found。

3.3异常处理的方法

若在响应中不仅要包括状态码,还要包含所产生的错误,此时的话,就不能将异常视为HTTP错误了,而是要按照处理请求的方式来处理异常了。

假设用户试图创建的Spittle与已创建的Spittle文本完全相同,那么SpittleRepository的save()方法将会抛出DuplicateSpittle Exception异常。这意味着SpittleController的saveSpittle()方法可能需要处理这个异常。

程序的处理:

     @RequestMapping(method=RequestMethod.POST)
     public String saveSpittle(SpittleForm form,Model model){
         try{
             spittleRepository.save(new Spittle(null,form,getMessage(),new Date()));
             return "redirect:/spittles";
         }catch(DuplicateSpittleException e){
             return "err/duplicate";
         }
     }

如果能让saveSpittle()方法只关注正确的路径,而让其他方法处理异常的话,那么它就能简单一些。

修改:

     @RequestMapping(method=RequestMethod.POST)
     public String saveSpittle(SpittleForm form,Model model){
             spittleRepository.save(new Spittle(null,form,getMessage(),new Date()));
             return "redirect:/spittles";
     }
     
     @ExceptionHandler(DuplicateSpittleException.class)
     public String handlerDuplicateSpittle(){
         return "err/duplicate";
     }

handleDuplicateSpittle()方法上添加了@ExceptionHandler注解,当抛出DuplicateSpittleException异常的时候,将会委托该方法来处理。它返回的是一个String,这与处理请求的方法是一致的,指定了要渲染的逻辑视图名,它能够告诉用户他们正在试图创建一条重复的条目。

对于@ExceptionHandler注解标注的方法来说,比较有意思的一点在于它能处理同一个控制器中所有处理器方法所抛出的异常。所以,尽管我们从saveSpittle()中抽取代码创建了handleDuplicateSpittle()方法,但是它能够处理SpittleController中所有方法所抛出的DuplicateSpittleException异常。我们不用在每一个可能抛出DuplicateSpittleException的方法中添加异常处理代码,这一个方法就涵盖了所有的功能。

3.4为控制器添加通知

如果多个控制器类中都会抛出某个特定的异常,那么你可能会发现要在所有的控制器方法中重复相同的@ExceptionHandler方法。或者,为了避免重复,我们会创建一个基础的控制器类,所有控制器类要扩展这个类,从而继承通用的@ExceptionHandler方法。

但是:Spring 3.2为这类问题引入了一个新的解决方案:控制器通知。控制器通知(controller advice)是任意带有@ControllerAdvice注解的类,这个类会包含一个或多个如下类型的方法:

@ExceptionHandler注解标注的方法;
    @InitBinder注解标注的方法;
    @ModelAttribute注解标注的方法。

在带有@ControllerAdvice注解的类中,以上所述的这些方法会运用到整个应用程序所有控制器中带有@RequestMapping注解的方法上。

@ControllerAdvice注解本身已经使用了@Component,因此@ControllerAdvice注解所标注的类将会自动被组件扫描获取到,就像带有@Component注解的类一样。

@ControllerAdvice最为实用的一个场景就是将所有的@ExceptionHandler方法收集到一个类中,这样所有控制器的异常就能在一个地方进行一致的处理。

img_26807728100f36ba6e13d835661f767b.png
img

如果任意的控制器方法抛出了DuplicateSpittleException,不管这个方法位于哪个控制器中,都会调用这个duplicateSpittleHandler()方法来处理异常。

四:跨重定向请求传递数据

当控制器方法返回的String值以“redirect:”开头的话,那么这个String不是用来查找视图的,而是用来指导浏览器进行重定向的路径。

具体来讲,正在发起重定向功能的方法该如何发送数据给重定向的目标方法呢?一般来讲,当一个处理器方法完成之后,该方法所指定的模型数据将会复制到请求中,并作为请求中的属性,请求会转发(forward)到视图上进行渲染。同一个请求,所以在转发的过程中,请求属性能够得以保存。

当控制器的结果是重定向的话,原始的请求就结束了,并且会发起一个新的GET请求。原始请求中所带有的模型数据也就随着请求一起消亡了。在新的请求属性中,没有任何的模型数据,这个请求必须要自己计算数据。

img_19b86f305672534914b4923f48bd71c0.png
img

有一些其他方案,能够从发起重定向的方法传递数据给处理重定向方法中:

使用URL模板以路径变量和/或查询参数的形式传递数据;
    通过flash属性发送数据。

4.1通过URL模板进行重定向

通过路径变量和查询参数传递数据看起来非常简单。以路径变量的形式传递了新创建Spitter的username。但是按照现在的写法,username的值是直接连接到重定向String上的。这能够正常运行,但是还远远不能说没有问题。当构建URL或SQL查询语句的时候,使用String连接是很危险的。

Spring还提供了使用模板的方式来定义重定向URL。

img_0b2df0be0d521ac20c2f40bf38c1cbb9.png
img

username作为占位符填充到了URL模板中,而不是直接连接到重定向String中,所以username中所有的不安全字符都会进行转义。这样会更加安全,这里允许用户输入任何想要的内容作为username,并会将其附加到路径上。

模型中所有其他的原始类型值都可以添加到URL中作为查询参数。作为样例,假设除了username以外,模型中还要包含新创建Spitter对象的id属性,那processRegistration()方法可以改写为如下的形式:

img_70daed821fc615d3178225e3bdef2d63.png
img

所返回的重定向String并没有太大的变化。但是,因为模型中的spitterId属性没有匹配重定向URL中的任何占位符,所以它会自动以查询参数的形式附加到重定向URL上。

如果username属性的值是habuma并且spitterId属性的值是42,那么结果得到的重定向URL路径将会是“/spitter/habuma?spitterId=42”。

通过路径变量和查询参数的形式跨重定向传递数据是很简单直接的方式,但它也有一定的限制。它只能用来发送简单的值,如String和数字的值。

4.2使用flash属性

Spitter对象要比String和int更为复杂。因此,我们不能像路径变量或查询参数那么容易地发送Spitter对象。它只能设置为模型中的属性。

模型数据最终是以请求参数的形式复制到请求中的,当重定向发生的时候,这些数据就会丢失。因此,我们需要将Spitter对象放到一个位置,使其能够在重定向的过程中存活下来。有个方案是将Spitter放到会话中。会话能够长期存在,并且能够跨多个请求。所以我们可以在重定向发生之前将Spitter放到会话中,并在重定向后,从会话中将其取出。当然,我们还要负责在重定向后在会话中将其清理掉。

Spring认为我们并不需要管理这些数据,相反,Spring提供了将数据发送为flash属性(flash attribute)的功能。按照定义,flash属性会一直携带这些数据直到下一次请求,然后才会消失。

Spring提供了通过RedirectAttributes设置flash属性的方法,这是Spring 3.1引入的Model的一个子接口。RedirectAttributes提供了Model的所有功能。

具体来讲,RedirectAttributes提供了一组addFlashAttribute()方法来添加flash属性。重新看一下processRegistration()方法

img_5e19e9dcbc7619fc13bd5cc48c9913ce.png
img

调用了addFlashAttribute()方法,并将spitter作为key,Spitter对象作为值。另外,我们还可以不设置key参数,让key根据值的类型自行推断得出:因为我们传递了一个Spitter对象给addFlashAttribute()方法,所以推断得到的key将会是spitter

在重定向执行之前,所有的flash属性都会复制到会话中。在重定向后,存在会话中的flash属性会被取出,并从会话转移到模型之中。

img_4d7388acd0e18247dd77947754d01ff5.png
img
img_495de3004f79f31b0e0ec4ef5691fef4.png
img

showSpitterProfile()方法所做的第一件事就是检查是否存有key为spitter的model属性。如果模型中包含spitter属性,那就什么都不用做了。这里面包含的Spitter对象将会传递到视图中进行渲染。但是如果模型中不包含spitter属性的话,那么showSpitterProfile()将会从Repository中查找Spitter,并将其存放到模型中。

如下在controller中写如下:

@RequestMapping("test")
    public String testDemo() {


        return "test";

    }

@RequestMapping("reset")
    public  String reset(Model model){

        model.addAttribute("test","test");

        model.addAttribute("username","张三");

        return "redirect:/stu/{test}";

}
img_5c46c6df8cc3531dca8eed30f3907138.gif
image
相关文章
|
6月前
|
前端开发 JavaScript Java
JAVAEE框架技术之4springMVC入门
JAVAEE框架技术之4springMVC入门
132 0
JAVAEE框架技术之4springMVC入门
|
11月前
|
XML 存储 Java
SpringMVC中支持的那些视图解析技术
SpringMVC中支持的那些视图解析技术
107 0
|
前端开发 Java Go
Spring MVC 中的数据验证技术
Spring MVC 中的数据验证技术
97 0
|
2月前
|
前端开发 安全 Java
技术进阶:使用Spring MVC构建适应未来的响应式Web应用
【9月更文挑战第2天】随着移动设备的普及,响应式设计至关重要。Spring MVC作为强大的Java Web框架,助力开发者创建适应多屏的应用。本文推荐使用Thymeleaf整合视图,通过简洁的HTML代码提高前端灵活性;采用`@ResponseBody`与`Callable`实现异步处理,优化应用响应速度;运用`@ControllerAdvice`统一异常管理,保持代码整洁;借助Jackson简化JSON处理;利用Spring Security增强安全性;并强调测试的重要性。遵循这些实践,将大幅提升开发效率和应用质量。
62 7
|
6月前
|
前端开发 Java Apache
JAVAEE框架技术之6-springMVC拦截器和文件上传功能
JAVAEE框架技术之6-springMVC拦截器和文件上传功能
99 0
JAVAEE框架技术之6-springMVC拦截器和文件上传功能
序-Servlet和SpringMVC的联系和区别-配置路径先想好使用的使用的方法,然后匹配的需要的技术
序-Servlet和SpringMVC的联系和区别-配置路径先想好使用的使用的方法,然后匹配的需要的技术
|
5月前
|
JSON Java 数据库
技术笔记:SpringMVC常用注解
技术笔记:SpringMVC常用注解
|
6月前
|
前端开发 Java API
饼干探秘:深入Spring MVC中获取Cookie数据的技术解析
饼干探秘:深入Spring MVC中获取Cookie数据的技术解析
66 3
|
6月前
|
JSON 前端开发 JavaScript
JAVAEE框架技术之5-springMVC参数绑定和异步交互
JAVAEE框架技术之5-springMVC参数绑定和异步交互
71 0
JAVAEE框架技术之5-springMVC参数绑定和异步交互
|
前端开发 Java 应用服务中间件
【小家Spring】高性能关键技术之---体验Spring MVC的异步模式(Callable、WebAsyncTask、DeferredResult) 基础使用篇(上)
【小家Spring】高性能关键技术之---体验Spring MVC的异步模式(Callable、WebAsyncTask、DeferredResult) 基础使用篇(上)
【小家Spring】高性能关键技术之---体验Spring MVC的异步模式(Callable、WebAsyncTask、DeferredResult) 基础使用篇(上)