4.4. @ControllerAdvice

@ControllerAdvice 就是@Controller 的增强版。@ControllerAdvice 主要用来处理全局数据, 一般搭配

@ExceptionHandler@ModelAttribute 以及@InitBinder 使用。

@ControllerAdvice 最常见的使用场景就是全局异常处理。在4.3 节向读者介绍过文件上传大小限制的配置,如果用户上传的文件超过了限制大小,就会抛出异常,此时可以通过@Controller Advice 结合@ExceptionHandler 定义全局异常捕获机制,代码如下:

@ControllerAdvice
public class CustomExceptionHandler {
  // 表明该方法用来处理MaxUploadSizeExceededException类型的异常
  // 如果想让该方法处理所有类型的异常, 只需将 MaxUploadSizeExceededException 改为Exception 即可   
@ExceptionHandler(MaxUploadSizeExceededException.class)
  // 方法的参数可以有异常实例、HttpServletResponse 以及HttpServletRequest 、Model 等
  public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {     resp.setContentType("text/html;charset=utf-8");     PrintWriter out = resp.getWriter();     out.write("上传文件大小超出限制!");     out.flush();     out.close();
    // 返回值可以是一段JSON 、一个ModelAndView 、一个逻辑视图名等   
}
}

PrintWriter 是一个输出流。返回的会是一个字符串而不是Json


 如果返回参数是一个ModelAndView ,假设使用的页面模板为Thymeleaf (注意添加Thymeleaf相关依赖〉,此时异常处理方法定义如下:

@ControllerAdvice
public class CustomExceptionHandler {
  @ExceptionHandler(MaxUploadSizeExceededException.class)
  public ModelAndView uploadException(MaxUploadSizeExceededException e) throws IOException {
    ModelAndView mv = new ModelAndView();
    mv.addObject("msg","上传文件大小超出限制!");
    // 指error.html
    mv.setViewName("error");     return mv;   } }

然后在resources/templates 目录下创建 error.html 文件,内容如下:效果和上面的相同

<!DOCTYPE html>
<html lang=@en" xmlns:th= "http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:text="${msg}"></div>
</body>
</html>

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

也可以在@ControllerAdvice 中配置全局数据,使用@ModelAttribute 注解进行配置,代码如下:
@ControllerAdvice
public class GlobalConfig {
   // value£性表示这条返回数据的key @ModelAttribute(value
= "info"public Map<String, String> userInfo () { HashMap<String , String> map = new HashMap<>();   map.put("username","罗贯中");   map.put("gender","男");   return map ;
  } }

此时在任意请求的Controller 中,通过方法参数中的Model 都可以获取info 的数据。

@GetMapping ("/hello")
@ResponseBody
public void hello(Model model) {
  Map<String, Object> map = model.asMap();
  Set<String> keySet = map.keySet();
  Iterator<String> iterator = keySet.iterator();
  while(iterator.hasNext()) {
    String key = iterator.next();
    Object value = map.get(key);
    System.out.println(key + ”>>>” + value );
  }
}

结果

 

 

 ................................................................................................................................................................

@Controller Ad vice 结合@InitBinder 还能实现请求参数预处理,即将表单中的数据绑定到实体
类上时进行一些额外处理。

public class Book {
    private String name ;
    private String author ;
    // 省略getter/setter
}
public class Author {   private String name;   private int age ;   // 省略getter/setter
}

在Controller 上需要接收两个实体类的数据,

@GetMapping ("/book" )
@ResponseBody
public String book(Book book, Author author) {
    return book.toString() + ”>>” + author.toString () ;
}

此时在参数传递时,两个实体类中的name 属性会混淆,@ControllerAdvice 结合@InitBinder可以顺利解决该问题。首先给Controller 中方法的参数添加@ModelAttribute注解

@GetMapping("/book")
@ResponseBody
public String book(@ModelAttribute("b") Book book,@ModelAttribute("a") Author author) {
  return book.toString() + ”>>” + author.toString();
}

然后配置@ControllerAdvice

@ControllerAdvice
public class GlobalConfig {
  @InitBinder("b")
  public void init(WebDataBinder binder) {
    binder.setFieldDefaultprefix("b.");
  }
  @InitBinder("a"
)   public void init2(WebDataBinder binder) {     binder.setFieldDefaultprefix("a.");
  }
}

 

在每个方法中给相应的Field 设直一个前缀,然后在浏览器中请求

http://localhost:8080/book?b.name=三国演义&b.author= 罗贯中&a.name=曹雪芹&a.age=48 ,即可成功地区分出name 属性。

不然的话都是 http://localhost:8080/book?name=三国演义&author= 罗贯中&name=曹雪芹&age=48 会无法区分是谁的name。

 

posted @ 2022-07-07 05:49  小白冲冲  阅读(76)  评论(0)    收藏  举报