問題:Controller層添加@NotEmpty或者@NotNull不生效,像下面這樣:
1 @ApiOperation(value = "測試")
2 @GetMapping("/test")
3 public String test(@NotEmpty(message = "desc不能為空") String name,
4 @NotNull(message = "age不能為空") Long age,
5 @NotEmpty(message = "desc不能為空") String desc) {
6 return "test";
7 }
但是這樣使用實體類,里面的屬性使用@NotEmpty就是生效的:
1 @ApiOperation(value = "測試")
2 @PostMapping("/test")
3 public String test(@RequestBody @Valid NoticeParam noticeParam) {
4 return "test";
5 }
6
7 @ApiModel(description = "測試")
8 @Data
9 public class NoticeParam {
10 @NotEmpty(message = "name不能為空")
11 private String name;
12
13 @NotNull(message = "desc不能為空")
14 private Long age;
15
16 @NotEmpty(message = "desc不能為空")
17 private String desc;
18 }
排查過程:
1.
檢查兩者的區別,發現參數前面有個@Valid,於是在參數前面同樣加了個@Valid
@ApiOperation(value = "測試")
@GetMapping("/test")
public String test(@Valid @NotEmpty(message = "desc不能為空") String name,
@NotNull(message = "age不能為空") Long age,
@NotEmpty(message = "desc不能為空") String desc) {
return "test";
}
postman測試:
name,age,desc都沒傳,但是仍然返回了成功。說明@Valid在這里不生效。

2.將@NotEmpty換成了@RequestParam,@RequestParam的源碼如下,可以看到required是默認為true的。
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
但是加上了@RequestParam之后,雖然是被攔截了,但是沒有將錯誤信息拋出來:

只是拋出了系統的默認錯誤:
而且@RequestParam不夠靈活,沒辦法自定義拋出錯誤信息。所以還是從@NotEmpty下手。
3.從網上找資料,發現使用@Validated標記Controller層,然后再在參數中添加@NotEmpty,就可以拋出錯誤:
@Api(tags = "通知接口")
@RestController
@RequestMapping("/api/notice")
@Validated
public class TestApi {
@ApiOperation(value = "測試")
@GetMapping("/test")
public String test(@NotEmpty(message = "name不能為空") String name,
@NotNull(message = "age不能為空") Long age,
@NotEmpty(message = "desc不能為空") String desc) {
return "test";
}
}
測試之后控制台打印出了錯誤

拋出來的錯誤並不是我們添加的錯誤信息,而是系統的默認錯誤。很明顯,系統中有對這種異常專門的處理,但是漏掉了ConstraintViolationException這種異常的錯誤。我通過打印日志排查,找到了處理系統錯誤的代碼:
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public BaseResult<?> exceptionHandler(Exception ex, HttpServletRequest request) {
log.error("", ex);
if (StringUtils.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(ex), "Broken pipe")) {
return null;
}
//處理異常
ErrorConstants error = ErrorHandler.handle(ex);
BaseResult<?> res = BaseResult.failure(error.getCode(), error.getMsg());
logResponse(res);
return res;
}
public class ErrorHandler {
public static ErrorConstants handle(Throwable e) {
//UNKNOWN_ERROR("未知錯誤", 101, "抱歉,我們遇到一點問題,請您稍后再試。"),
ErrorConstants error = ErrorConstants.UNKNOWN_ERROR;
//異常處理判斷,項目代碼不便展示
...
//處理ConstraintViolationException異常時,前面的if都沒進去,默認給了系統錯誤。
return error;
}
}
處理方式:
添加一個@ExceptionHandler針對ConstraintViolationException的處理,代碼如下:
@ExceptionHandler({ConstraintViolationException.class})
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResult<?> bindExceptionHandler(ConstraintViolationException ex) {
log.error("", ex);
String message = ErrorConstants.ILLEGAL_PARAM.getMsg();
//取出@NotEmpty后面message的自定義錯誤信息
Optional<ConstraintViolation<?>> aa = ex.getConstraintViolations().stream().findFirst();
if (aa.isPresent()) {
message = aa.get().getMessage();
}
//賦值並拋出
BaseResult<?> res = BaseResult.failure(ErrorConstants.ILLEGAL_PARAM.getCode(), message);
logResponse(res);
return res;
}
效果:

結論:
1.@ExceptionHandler用於處理全局異常的,也可以處理單獨的異常類。
2.上面在Controller類上面使用@Validated是生效的,但是使用@Valid是不生效的,原因還不清楚。
3.@RequestParam未直接拋出對應異常,和@NotEmpty處理方式一樣,可以加個@ExceptionHandler({MissingServletRequestParameterException.class})可以拋出異常。如下
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'name' is not present