Kotlin 的 ControllerAdvice
Posted
技术标签:
【中文标题】Kotlin 的 ControllerAdvice【英文标题】:ControllerAdvice for Kotlin 【发布时间】:2020-11-15 04:25:40 【问题描述】:我想为验证异常创建ControllerAdvice
,我正在使用 Webflux、Kotlin 和 jackson-module-kotlin。
我尝试过使用以下传统代码:
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException::class)
fun handleValidationExceptions(
ex: MethodArgumentNotValidException): Map<String, String?>?
val errors: MutableMap<String, String?> = HashMap()
ex.bindingResult.allErrors.forEach(Consumer error: ObjectError ->
val fieldName = (error as FieldError).field
val errorMessage = error.getDefaultMessage()
errors[fieldName] = errorMessage
)
return errors
但它不能正常工作,默认响应是:
"timestamp": "2020-07-25T10:19:00.023+00:00",
"path": "/boarding/subscribeUserWithSoloWorkspace",
"status": 400,
"error": "Bad Request",
"message": "Failed to read HTTP message",
"requestId": "62f1e90a-1",
"trace": "org.springframework.core.codec.DecodingException: JSON decoding error: Instantiation of [simple type, class co.ashiyane.flare.domains.User] value failed for JSON property mobileNumber due to missing (therefore NULL) value for creator parameter mobileNumber which is a non-nullable type; nested exception is com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class co.ashiyane.flare.domains.User] value failed for JSON property mobileNumber due to missing (therefore NULL) value for creator parameter mobileNumber which is a non-nullable type\n at [Source: (io.netty.buffer.ByteBufInputStream); line: 4, column: 5] (through reference chain: co.ashiyane.flare.domains.supdomains.UserAndWorkspace[\"user\"]->co.ashiyane.flare.domains.User[\"mobileNumber\"])\n\tat org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:215)\n\tat org.springframework.http.codec.json.AbstractJackson2Decoder.decode(AbstractJackson2Decoder.java:173)\n\tat org.springframework.http.codec.json.AbstractJackson2Decoder.lambda$decodeToMono$1(AbstractJackson2Decoder.java:159)\n\tat reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118)\n\tat reactor.core.publisher.FluxContextStart$ContextStartSubscriber.onNext(FluxContextStart.java:96)\n\tat reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:287)\n\tat reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:330)\n\tat reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1782)\n\tat reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:152)\n\tat reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)\n\tat reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:252)\n\tat reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:136)\n\tat reactor.netty.channel.FluxReceive.terminateReceiver(FluxReceive.java:427)\n\tat reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:210)\n\tat reactor.netty.channel.FluxReceive.request(FluxReceive.java:121)\n\tat reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:155)\n\tat reactor.core.publisher.FluxPeek$PeekSubscriber.request(FluxPeek.java:130)\n\tat reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:155)\n\tat reactor.core.publisher.MonoCollect$CollectSubscriber.onSubscribe(MonoCollect.java:112)\n\tat reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:86)\n\tat reactor.core.publisher.FluxPeek$PeekSubscriber.onSubscribe(FluxPeek.java:163)\n\tat reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:86)\n\tat reactor.netty.channel.FluxReceive.startReceiver(FluxReceive.java:300)\n\tat reactor.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:138)\n\tat io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)\n\tat io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:384)\n\tat io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)\n\tat io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)\n\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\tat java.base/java.lang.Thread.run(Thread.java:832)\nCaused by: com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class co.ashiyane.flare.domains.User] value failed for JSON property mobileNumber due to missing (therefore NULL) value for creator parameter mobileNumber which is a non-nullable type\n at [Source: (io.netty.buffer.ByteBufInputStream); line: 4, column: 5] (through reference chain: co.ashiyane.flare.domains.supdomains.UserAndWorkspace[\"user\"]->co.ashiyane.flare.domains.User[\"mobileNumber\"])\n\tat com.fasterxml.jackson.module.kotlin.KotlinValueInstantiator.createFromObjectWith(KotlinValueInstantiator.kt:112)\n\tat com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:202)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:490)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)\n\tat com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:542)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:535)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:419)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)\n\tat com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)\n\tat com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2057)\n\tat com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1431)\n\tat org.springframework.http.codec.json.AbstractJackson2Decoder.decode(AbstractJackson2Decoder.java:168)\n\t... 29 more\n"
消息中还有DecodingException
和MissingKotlinParameterException
等其他异常,但我也处理不了!
【问题讨论】:
【参考方案1】:我已经通过关注ControllerAdvice
解决了这个问题:
@ExceptionHandler(value = [ServerWebInputException::class])
@ResponseBody
fun onException(exception: ServerWebInputException): Mono<ResponseEntity<ClientAcknowledgement>>
val parameterName = (exception.rootCause as MissingKotlinParameterException).parameter.name // id
val parameterType = (exception.rootCause as MissingKotlinParameterException).parameter.type // ObjectId
val fieldName = (exception.rootCause as MissingKotlinParameterException).path[0].fieldName // in User part
return Mono.just(ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ClientAcknowledgement("there is a missing parameter in your request, check your request body." +
" detail : missing $parameterName ($parameterType) type in $fieldName")))
【讨论】:
以上是关于Kotlin 的 ControllerAdvice的主要内容,如果未能解决你的问题,请参考以下文章