要求:使用自定义异常 ,返回友好提示
结果如图,当价格未输入时,提示400,并且提示具体错误信息:
1,pojo 只有id name price
2, 控制层
@RestController
public class ItemController {
@PostMapping("item")
public ResponseEntity<Item> save(Item item){
item.setId(1L);
if (item.getPrice()==null){
throw new LyException(ExceptionEnum.PRICE_CANNOT_BE_NULL);
}
return ResponseEntity.status(HttpStatus.CREATED).body(item);
}
}
使用spring的ResponseEntity处理HTTP响应。
搜索资料:
1.ResponseEntity的优先级高于@ResponseBody。在不是ResponseEntity的情况下才去检查有没有@ResponseBody注解。如果响应类型是ResponseEntity可以不写@ResponseBody注解,写了也没有关系。
2.ResponseEntity 是在 org.springframework.http.HttpEntity 的基础上添加了http status code(http状态码),用于RestTemplate以及@Controller的HandlerMethod。它在Controoler中或者用于服务端响应时,作用是和@ResponseStatus与@ResponseBody结合起来的功能一样的。用于RestTemplate时,它是接收服务端返回的http status code 和 reason的。 总结: 简单粗暴的讲 @ResponseBody可以直接返回Json结果, @ResponseEntity不仅可以返回json结果,还可以定义返回的HttpHeaders和HttpStatus
3,自定义的异常 (继承了RuntimeException ,重写了2个构造,throwable是Exception 的父类)
@Getter
public class LyException extends RuntimeException{
private int status;
public LyException(ExceptionEnum em) {
super(em.getMessage());
this.status = em.getStatus();
}
public LyException(ExceptionEnum em, Throwable cause) {
super(em.getMessage(),cause);
this.status = em.getStatus();
}
}
4,异常的参数 是一个enum (应该是上一步之前)
用户抛出异常时,就必须传递两个内容:
-
异常信息
-
异常状态码
但是RuntimeException
是无法接受状态码的,只能接受异常的消息,所以我们需要做两件事情:
-
自定义异常,来接受状态码、异常消息
-
状态码与异常消息可能会重复使用,我们通过枚举来把这些信息变为常量
枚举:把一件事情的所有可能性列举出来。在计算机中,枚举也可以叫多例,单例是多例的一种情况 。
@Getter
public enum ExceptionEnum {
PRICE_CANNOT_BE_NULL(400,"价格不能为空");
private int status;
private String message;
ExceptionEnum(int status, String message) {
this.status = status;
this.message = message;
}
}
5,异常的通知类(用来指定哪个异常)
@ControllerAdvice
@Slf4j
public class BasicExceptionAdvice {
//捕获异常处理 自定义异常
@ExceptionHandler(LyException.class)
public ResponseEntity<ExceptionResult> LyException(LyException le){
// 从异常中获取友好提示信息
return ResponseEntity.status(le.getStatus()).body(new ExceptionResult(le));
}
}
Spring 在3.2版本后面增加了一个ControllerAdvice注解。
ControllerAdvice拆分开来就是Controller Advice,关于Advice,在Spring Aop中,其是用于封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。
这里ContrllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行“切面”环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。
@ControllerAdvice是在类上声明的注解,其用法主要有三点:
结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的;
结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的;
结合方法型注解@ModelAttribute,表示其标注的方法将会在目标Controller方法执行之前执行。
@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持。
解读:
@ControllerAdvice
:默认情况下,会拦截所有加了@Controller
的类
@ExceptionHandler(LyException.class)
:作用在方法上,声明要处理的异常类型,可以有多个,这里指定的是LyException
。被声明的方法可以看做是一个SpringMVC的Handler
:
参数是要处理的异常,类型必须要匹配
返回结果可以是
ModelAndView
、ResponseEntity
等,基本与handler
类似这里等于从新定义了返回结果,我们可以随意指定想要的返回类型。此处使用了String
一个被@ExceptionHandler注解修饰的方法,就能捕获controller层所有抛出的InsertMessageException类型异常,并对异常进行相应的处理
如果想一个方法处理多种类型异常,就需要在@ExceptionHandler的括号中用大括号添加所想要处理的异常类型,用”,”隔开
例如:@ExceptionHandler({InsertException.class,DeleteException.class})使用 @ControllerAdvice,不用任何的配置,只要把这个类放在项目中,Spring能扫描到的地方。就可以实现全局异常的回调
6,异常返回结果的友好(应该是上一步之前)
@Getter
public class ExceptionResult {
private int status;
private String message;
private String timestamp;
public ExceptionResult(LyException e) {
this.status = e.getStatus();
this.message = e.getMessage();
this.timestamp = DateTime.now().toString("yyyy-MM-dd HH:mm:ss");
}
}