Spring MVC 的异常解析
核心接口
• HandlerExceptionResolver
实现类
• SimpleMappingExceptionResolver
• DefaultHandlerExceptionResolver
• ResponseStatusExceptionResolver
• ExceptionHandlerExceptionResolver
Spring MVC 的异常解析HandlerExceptionResolver这个核心接口和其他几个实现类完成的,ResponseStatusExceptionResolver 处理 的是带ResponseStatus注解的一些方法和类,我们可以在异常类上面添加ResponseStatus这个注解,以表明在抛出这个异常类的时候,我的http响应码是什么。
DispatcherServlet如何处理异常
首先进入DispatcherServlet,找到doService方法。在doService方法中,在处理doDispatch时,我们进入doDispatch,在doDispatch中,我们可以看到,如果我们抛出异常,这些异常会被捕获住,然后,放在dispatchException当中,使用processDispatchResult处理结果,进入processDispatchResult,在处理结果的时候,就会去看,有没有传入一个异常,如果有传入异常,就会对这个异常进行处理,使用processHandlerException方法进行处理,进入processHandlerException,可以看到,通过handlerExceptionResolvers这一个链式解析,检验这个resolver能不能处理这个异常,如果可以处理,处理完毕之后,返回一个ModelAndView,使用ModelAndView做一个视图的呈现。
异常处理方法
处理方法
• @ExceptionHandler
添加位置
• @Controller / @RestController
• @ControllerAdvice / @RestControllerAdvice
在一个方法上添加ExceptionHandler注解,这样标明我们这个方法是来处理异常的。可以加在我们的Controller 当中,在Controller 类当中添加一个带有ExceptionHandler注解的方法。我们也可以做一个类似于aop的动作,去编写一个ControllerAdvice ,这里就相当于aop的一个拦截器,它会对我们所有的Controller做一个拦截,在ControllerAdvice 里面添加一个带有ExceptionHandler注解的方法。
注意:我们在ControllerAdvice 中添加的ExceptionHandler方法的优先级低于Controller 中添加的ExceptionHandler方法。
实例
目录结构如下:
需要修改的代码:
@Controller
@RequestMapping("/coffee")
@Slf4j
public class CoffeeController {
@Autowired
private CoffeeService coffeeService;
@PostMapping(path = "/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
public Coffee addCoffee(@Valid NewCoffeeRequest newCoffee,
BindingResult result) {
if (result.hasErrors()) {
log.warn("Binding Errors: {}", result); //当绑定出错的时候 返回一个FormValidationException的异常
throw new FormValidationException(result);
}
return coffeeService.saveCoffee(newCoffee.getName(), newCoffee.getPrice());
}
@PostMapping(path = "/", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
public Coffee addJsonCoffee(@Valid @RequestBody NewCoffeeRequest newCoffee,
BindingResult result) {
if (result.hasErrors()) {
log.warn("Binding Errors: {}", result); //当绑定出错的时候 ValidationException 自带的
throw new ValidationException(result.toString());
}
return coffeeService.saveCoffee(newCoffee.getName(), newCoffee.getPrice());
}
// @PostMapping(path = "/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
// @ResponseBody
// @ResponseStatus(HttpStatus.CREATED)
// public Coffee addCoffeeWithoutBindingResult(@Valid NewCoffeeRequest newCoffee) {
// return coffeeService.saveCoffee(newCoffee.getName(), newCoffee.getPrice());
// }
// @PostMapping(path = "/", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
// @ResponseBody
// @ResponseStatus(HttpStatus.CREATED)
// public Coffee addJsonCoffeeWithoutBindingResult(@Valid @RequestBody NewCoffeeRequest newCoffee) {
// return coffeeService.saveCoffee(newCoffee.getName(), newCoffee.getPrice());
// }
@PostMapping(path = "/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
public List<Coffee> batchAddCoffee(@RequestParam("file") MultipartFile file) {
List<Coffee> coffees = new ArrayList<>();
if (!file.isEmpty()) {
BufferedReader reader = null;
try {
reader = new BufferedReader(
new InputStreamReader(file.getInputStream()));
String str;
while ((str = reader.readLine()) != null) {
String[] arr = StringUtils.split(str, " ");
if (arr != null && arr.length == 2) {
coffees.add(coffeeService.saveCoffee(arr[0],
Money.of(CurrencyUnit.of("CNY"),
NumberUtils.createBigDecimal(arr[1]))));
}
}
} catch (IOException e) {
log.error("exception", e);
} finally {
IOUtils.closeQuietly(reader);
}
}
return coffees;
}
@GetMapping(path = "/", params = "!name")
@ResponseBody
public List<Coffee> getAll() {
return coffeeService.getAllCoffee();
}
@RequestMapping(path = "/{id}", method = RequestMethod.GET)
@ResponseBody
public Coffee getById(@PathVariable Long id) {
Coffee coffee = coffeeService.getCoffee(id);
log.info("Coffee {}:", coffee);
return coffee;
}
@GetMapping(path = "/", params = "name")
@ResponseBody
public Coffee getByName(@RequestParam String name) {
return coffeeService.getCoffee(name);
}
}
@ResponseStatus(HttpStatus.BAD_REQUEST) //当碰到这个异常的时候 http状态码为400的BAD_REQUEST
@Getter
@AllArgsConstructor
public class FormValidationException extends RuntimeException {
private BindingResult result;
}
@RestControllerAdvice //是@ControllerAdvice和@ResponseBody的合并。此注解通过对异常的拦截实现的统一异常返回处理
public class GlobalControllerAdvice {
@ExceptionHandler(ValidationException.class) //专门处理ValidationException这个异常 可以传入很多类型
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> validationExceptionHandler(ValidationException exception) {
Map<String, String> map = new HashMap<>();
map.put("message", exception.getMessage());
return map;
}
}
实验效果
打开postman
我们以表单的方式创建一个咖啡(错误情况):
提供表单做的错误处理,是springmvc来做的处理,即,使用的是FormValidationException
我们以json的形式创建一个咖啡(错误情况):
我们可以看到,返回的是message错误信息,使用了GlobalControllerAdvice进行了处理。
来源:CSDN
作者:L# S@
链接:https://blog.csdn.net/weixin_43790623/article/details/103652534