Springboot笔记<8>异常处理 文件上传
文件上传
springboot可以直接使用 org.springframework.web.multipart.MultipartFile实现文件上传功能。
1.创建form表单:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>hah</title>
</head>
<body>
<text>单文件上传</text>
<form method="post" action="/singleUpload" enctype="multipart/form-data">
<input type="file" name="headerImg"><br>
<input type="submit" value="提交">
</form>
<text>多文件上传</text>
<form method="post" action="/multiUpload" enctype="multipart/form-data">
<input type="file" name="photos"><br>
<input type="file" name="photos"><br>
<input type="file" name="photos"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
2.yaml配置:
spring:
servlet:
multipart:
enabled: true
file-size-threshold: 2KB
max-request-size: 215MB
3.官网对于上传文件yaml配置参数的解释:
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.(单个文件的最大值)
spring.servlet.multipart.max-file-size=200MB
# Max Request Size(上传文件总的最大值)
spring.servlet.multipart.max-request-size=215MB
## File Storage Properties
# All files uploaded through the REST API will be stored in this directory
file.upload-dir=/Users/callicoder/uploads
4.写一个Controller验证:
- 单文件上传
- 多文件上传
@RestController
@Slf4j
public class UploadController {
//上传单个文件
@PostMapping("/singleUpload")
public String multiUpload(
@RequestPart("headerImg") MultipartFile headerImg) throws IOException {
log.info("上传的信息:headerImg={}",
headerImg.getSize());
if (!headerImg.isEmpty()) {
//保存到文件服务器,OSS服务器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("E:\\cache\\" + originalFilename));
}
return "singleUpload";
}
//上传多个文件
@PostMapping("/multiUpload")
public String singleUpload(
@RequestPart("photos") MultipartFile[] photos) throws IOException {
log.info("上传的信息:photos={}",
photos.length);
if (photos.length > 0) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("E:\\cache\\" + originalFilename));
}
}
}
return "multiUpload";
}
}
异常处理
默认情况下,Spring Boot为两种情况提供了不同的响应方式。
一种是浏览器客户端请求一个不存在的页面或服务端处理发生异常时,一般情况下浏览器默认发送的请求头中Accept: text/html,所以Spring Boot默认会响应一个html文档内容,称作“Whitelabel Error Page”。另一种是使用Postman等调试工具发送请求一个不存在的url或服务端处理发生异常时,Spring Boot会返回类似如下的Json格式字符串信息。Spring Boot 默认提供了程序出错的结果映射路径/error。这个/error请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自客户端浏览器(浏览器通常默认自动发送请求头内容Accept:text/html)还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容。
自定义错误页面
error.html
先从最简单的开始,直接在/resources/templates
下面创建error.html就可以覆盖默认的Whitelabel Error Page
的错误页面,我项目用的是thymeleaf模板,对应的error.html代码如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
动态error错误页面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>
静态错误页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
静态404错误页面
</body>
</html>
静态页面资源目录下存放404.html,发生404错误时会访问到404.html
动态错误页面:
存放路径/resources/templates/error下,比如/resources/templates/error/4xx.html,/resources/templates/error/404.html
比如创建/resources/templates/error/4xx.html,则发生4打头的错误码时都会访问4xx.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
4xx动态error错误页面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>
页面优先级
- error.html会覆盖默认的 whitelabel Error Page 错误提示
- 静态错误页面优先级别比error.html高
- 动态模板错误页面优先级比静态错误页面高
重写ErrorController
在静态处理代码分析的时候我们说到了项目启动时候就会自动加载默认异常处理配置ErrorMvcAutoConfiguration,会默认加载BasicErrorController作为异常处理的控制器。那么我们想要自己定义处理逻辑的话,我们就直接覆盖掉BasicErrorController,然后重写一个就好了。
继承ErrorPageRegistrar
ErrorPageRegistrar是一个错误页面的注册器,在ErrorMvcAutoConfiguration中我们依然可以找到对应的源码代码,它默认帮我们写了一个ErrorPageCustomizer用于处理注册的错误页面,我们可以看到启动时候,会默认先把/error页面注册进去。
@ControllerAdvice注解处理异常
Spring正好提供了一个非常方便的异常处理方案——控制器通知 (@ControllerAdvice 或 @RestControllerAdvice),它将所有控制器作为一 个切面,利用切面技术来实现。可以减少因忘记写catch而出现错误的概率通过基于@ControllerAdvice或@RestContollerAdvice的注解可以对异常进行全局统一处理,默认对所有的Controller有效。如果要限定生效范围,则可以使用@ControllerAdvice支持的限定范围方式。
不一定需要组合来一起用,当我们需要在某个特定控制器里面处理特定异常时候,我们的@ExceptionHandler可以直接写在controller中,这样的话@ExceptionHandler就只能处理这个单个controller的异常。那有时候我们想全局处理所有的控制器的异常,于是就有了@ControllerAdvice,它会控制器增强,会应用到所有的controller上,这样就实现了我们想要的全局异常处理。
@ControllerAdvice支持的限定范围
- 按注解:@ControllerAdvice(annotations = RestController .class)。
- 按包名:@ControllerAdvice("org.example.controller")。
- 按类型:@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})。
示例1:处理全局异常Exception.class
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
String handleException(){
return "Exception";
}
}
方法 handleException() 就会处理所有 Controller 层抛出的 Exception 及其子类的异常。被 @ExceptionHandler 注解的方法的参数列表里,还可以声明很多种类型的参数。
示例2:处理多种异常
访问/exceptionMethod会抛出ArithmeticException,GlobalExceptionHandler对ArithmeticException和Exception都进行了处理,会优先调用handleArithmeticException。
@Slf4j
@RestController
public class ExceptionController {
@RequestMapping("/exceptionMethod")
public String exceptionMethod(Model model) throws Exception {
model.addAttribute("msg", "没有抛出异常");
int num = 1/0; //a处
log.info(String.valueOf(num));
return "home";
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
String handleException(){
return "Exception";
}
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
String handleArithmeticException(){
return "ArithmeticException";
}
@ExceptionHandler(value={java.lang.NullPointerException.class})
ModelAndView nullPointerExceptionHandler(Exception e){
System.out.println("goto NullPointerExceptionPage");
ModelAndView mv=new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("NullPointerExceptionPage");
return mv;
}
}
未经作者同意请勿转载
本文来自博客园作者:aixueforever,原文链接:https://www.cnblogs.com/aslanvon/p/15715207.html