|
13 | 13 | Latest Version:
|
14 | 14 | [](https://central.sonatype.com/search?q=g:cn.sticki%20a:spel-validator)
|
15 | 15 |
|
16 |
| -大多少情况下,你只需要添加以下两个依赖: |
| 16 | +大多数情况下,你只需要添加以下两个依赖: |
17 | 17 |
|
18 | 18 | ```xml
|
19 | 19 | <dependencys>
|
@@ -87,7 +87,7 @@ public class SimpleExampleParamVo {
|
87 | 87 |
|
88 | 88 | `@SpelValid` 注解包含一个属性 `condition`,支持 SpEL 表达式。
|
89 | 89 |
|
90 |
| -当 **表达式的算结果为true** 时,表示开启校验,默认情况下开启校验。 |
| 90 | +当 **表达式的算结果为true** 时,表示开启校验,默认情况下是开启的。 |
91 | 91 |
|
92 | 92 | ```java
|
93 | 93 | @Data
|
@@ -225,17 +225,121 @@ public class TestParamVo2 {
|
225 | 225 | }
|
226 | 226 | ```
|
227 | 227 |
|
228 |
| -## 处理异常 |
229 |
| - |
230 |
| -待补充 |
| 228 | +## 处理约束异常 |
231 | 229 |
|
232 | 230 | 当校验失败时,本组件会将异常信息上报到 `javax.validation` 的异常体系中。
|
233 | 231 |
|
234 |
| -所以正常情况下,你无需添加额外的异常处理逻辑。 |
| 232 | +正常情况下,你只需要处理 `org.springframework.web.bind.MethodArgumentNotValidException` |
| 233 | +和 `org.springframework.validation.BindException` 这两个校验异常类就好了 ,而无需额外处理本组件的异常信息。 |
| 234 | + |
| 235 | +事实上,`MethodArgumentNotValidException` 继承自 `BindException`,只需要处理 `BindException` 就可以了。 |
| 236 | + |
| 237 | +```java |
| 238 | +@RestControllerAdvice |
| 239 | +public class ControllerExceptionAdvice { |
| 240 | + |
| 241 | + @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class}) |
| 242 | + public Resp<Void> handleBindException(BindException ex) { |
| 243 | + String msg = ex.getFieldErrors().stream() |
| 244 | + .map(error -> error.getField() + " " + error.getDefaultMessage()) |
| 245 | + .reduce((s1, s2) -> s1 + "," + s2) |
| 246 | + .orElse(""); |
| 247 | + return new Resp<>(400, msg); |
| 248 | + } |
| 249 | + |
| 250 | +} |
| 251 | +``` |
| 252 | + |
| 253 | +## 处理业务异常 |
| 254 | + |
| 255 | +由于本组件支持 [调用静态方法](spel.md#调用静态方法) 和 [调用Spring Bean方法](spel.md#调用-spring-bean),故在校验过程中可能会抛出除约束异常以外的其他业务异常。 |
| 256 | + |
| 257 | +### 举一个不太恰当的例子 |
| 258 | + |
| 259 | +以下是一个枚举类,它包含了一个静态方法,用于根据code获取枚举值,如果获取不到则抛出业务异常: |
235 | 260 |
|
236 | 261 | ```java
|
| 262 | +@Getter |
| 263 | +public enum ExampleEnum { |
| 264 | + |
| 265 | + XXX(1); |
| 266 | + |
| 267 | + private final Integer code; |
| 268 | + |
| 269 | + ExampleEnum(Integer code) { |
| 270 | + this.code = code; |
| 271 | + } |
| 272 | + |
| 273 | + /** |
| 274 | + * 通过code获取枚举值,如果code不存在则抛出业务异常 |
| 275 | + */ |
| 276 | + public static ExampleEnum getByCode(Integer code) { |
| 277 | + for (ExampleEnum value : values()) { |
| 278 | + if (value.code.equals(code)) { |
| 279 | + return value; |
| 280 | + } |
| 281 | + } |
| 282 | + throw new BusinessException(400, "枚举值不合法"); |
| 283 | + } |
| 284 | + |
| 285 | +} |
237 | 286 | ```
|
238 | 287 |
|
| 288 | +以下是一个参数类,它包含了一个枚举字段校验,在表达式中引用了上面的枚举类: |
| 289 | + |
| 290 | +```java |
| 291 | +@Data |
| 292 | +@SpelValid |
| 293 | +public class ParamTestBean { |
| 294 | + |
| 295 | + /** |
| 296 | + * 枚举值校验 |
| 297 | + * <p> |
| 298 | + * 通过静态方法调用,校验枚举值是否存在 |
| 299 | + */ |
| 300 | + @SpelAssert(assertTrue = "T(cn.sticki.validator.spel.enums.ExampleEnum).getByCode(#this.testEnum)") |
| 301 | + private Integer testEnum; |
| 302 | + |
| 303 | +} |
| 304 | +``` |
| 305 | + |
| 306 | +当 `ParamTestBean` 校验失败时,我们希望它抛出一个业务异常 `BusinessException`,但实际上会得到一个 `ValidationException`: |
| 307 | + |
| 308 | + |
| 309 | + |
| 310 | +由于本组件的特殊性,所有抛出的异常信息最终都会被我们下层的校验器捕获,然后包一层 `javax.validation.ValidationException` 再抛出。 |
| 311 | + |
| 312 | +要从框架层面去解决这个问题,只能够脱离 `javax.validation` 的规范和 `hibernate` 的执行器来进行校验, |
| 313 | +目前看来这样做的成本比较大,且会带来一些其他的影响,故暂时不考虑这样做。 |
| 314 | + |
| 315 | +### 解决方案 |
| 316 | + |
| 317 | +当捕获到 `ValidationException` 时,首先判断下 `e.getCause()` 的类型是不是自己项目中的业务异常基类,如果是业务异常的类型,就丢给对应的方法去处理,像这样: |
| 318 | + |
| 319 | +```java |
| 320 | +@RestControllerAdvice |
| 321 | +public class ControllerExceptionAdvice { |
| 322 | + |
| 323 | + @ExceptionHandler({BusinessException.class}) |
| 324 | + public Resp<Void> handleBusinessException(BusinessException ex) { |
| 325 | + return new Resp<>(ex.getCode(), ex.getMessage()); |
| 326 | + } |
| 327 | + |
| 328 | + @ExceptionHandler({ValidationException.class}) |
| 329 | + public Resp<Void> handleValidationException(ValidationException ex) { |
| 330 | + if (ex.getCause() instanceof BusinessException) { |
| 331 | + return handleBusinessException((BusinessException) ex.getCause()); |
| 332 | + } |
| 333 | + return new Resp<>(500, "system error"); |
| 334 | + } |
| 335 | + |
| 336 | +} |
| 337 | +``` |
| 338 | + |
| 339 | +当然这种方案也有缺点,需要将多种不同的异常类型都进行特殊处理,比较麻烦。 |
| 340 | + |
| 341 | +本人不才,目前只能想到这种方案,如果你有更好的解决方案,欢迎到 GitHub 提 [issue](https://github.com/stick-i/spel-validator/issues) |
| 342 | + |
239 | 343 | ## 开启对 Spring Bean 的支持
|
240 | 344 |
|
241 | 345 | 默认情况下,解析器无法识别 SpEL 表达式中的 Spring Bean。
|
|
0 commit comments