惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
Google DeepMind News
Google DeepMind News
Scott Helme
Scott Helme
NISL@THU
NISL@THU
T
Tor Project blog
T
Tenable Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
P
Privacy International News Feed
Cyberwarzone
Cyberwarzone
Project Zero
Project Zero
S
Schneier on Security
C
Cybersecurity and Infrastructure Security Agency CISA
P
Proofpoint News Feed
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
O
OpenAI News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
美团技术团队
Hugging Face - Blog
Hugging Face - Blog
V2EX - 技术
V2EX - 技术
H
Help Net Security
C
Check Point Blog
T
The Exploit Database - CXSecurity.com
Forbes - Security
Forbes - Security
人人都是产品经理
人人都是产品经理
U
Unit 42
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
D
Docker
I
InfoQ
Schneier on Security
Schneier on Security
K
Kaspersky official blog
罗磊的独立博客
Cisco Talos Blog
Cisco Talos Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
Security Affairs
Microsoft Security Blog
Microsoft Security Blog
GbyAI
GbyAI
雷峰网
雷峰网
G
GRAHAM CLULEY
Cloudbric
Cloudbric
IT之家
IT之家
Stack Overflow Blog
Stack Overflow Blog
AI
AI
L
LINUX DO - 热门话题
云风的 BLOG
云风的 BLOG
博客园_首页
T
Threat Research - Cisco Blogs
S
Secure Thoughts
有赞技术团队
有赞技术团队

czp's blog

Ubuntu 下使用 zsh · HonKit 使用 VNC 来远程管理服务器 · HonKit Linux 下无限试用 JetBrains · HonKit 使用 KMS 激活 Windows · HonKit 为 KVM 中的 Windows 虚拟机启用 VirtIO · HonKit 用 Spring Native 拯救微服务 · HonKit 正确调试 PHP · HonKit Ubuntu 增加最大文件打开数量限制 · HonKit 将 Minecraft 服务端编译到 native · HonKit 用凝聚力购买 P 社游戏 DLC · HonKit 模拟超星网课 Android 客户端 · HonKit 模拟 Bilibili Android 客户端 · HonKit 模拟哔咔 Android 客户端 · HonKit 异步是什么 · HonKit GCP Pub/Sub push 至 IAP 保护的 Endpoint · HonKit 部署 Spring Native 到 GCP App Engine · HonKit 多个 Gradle SubProject 同时编译导致 CI/CD 内存耗尽 · HonKit Google Cloud Build 运行 DIND · HonKit 在 Google Storage 中部署 SPA · HonKit Terraform 不自动更新 CloudRun service · HonKit Telegram Bot 收不到普通群聊消息的问题 · HonKit 加密货币交易 · HonKit 挖掘以太坊 · HonKit 挖掘门罗币 · HonKit 小明学英语 · HonKit 卖程序的小女孩 · HonKit 一个测试工程师走进一家酒吧 · HonKit 我给你们讲个 TCP 笑话吧! · HonKit 我给你们讲个 UDP 笑话吧! · HonKit 格局打开 · HonKit c 语言常见未定义行为 · HonKit 在 Kotlin 使用 SpaceEngineers Remote API · HonKit Kotlin 协程 · HonKit 在 Spring Boot 中正确注册 Jackson Module · HonKit Spring Boot Jpa 使用 Google Cloud SQL · HonKit Spring Boot 中配置单页应用 · HonKit Spring Boot 无法加载 ClasspathResource 问题 · HonKit 正确打包 Spring Boot 到 war · HonKit czp's blog · HonKit 鸣谢列表 · HonKit 友链 · HonKit
在 Spring Boot 中处理 MissingKotlinParameterException · HonKit
2026-04-11 · via czp's blog

本文撰写时的 spring 版本: Spring Boot 2.5.5.RELEASE

众所周知 kotlin 有空安全功能, 不能将一个非空类型变量赋值为 null. 这在正常代码中是没有问题的. 但是如果变量是通过反射进行赋值的, 比如说反序列化库, 诸如 jackson, gson 等就会有问题.

如果反序列化库直接用 Unsafe 来生成实例, 最终会得到一个空了的非空字段. 如果反序列化库用构造器来初始化, 将抛出 NullPointerException. 而如果反序列化库支持使用 kotlin 语言, 将会有对应的异常. 例如 jackson 在安装了 kotlin module 后将对传入的实际值是 null, 但是字段类型不可空的成员抛出 MissingKotlinParameterException .

如果只是使用反序列化库, 这一切还不是特别大的问题, 简单地 try catch 就可以处理异常了. 但是如果是在 spring 中碰到这个问题, 反序列化的代码在框架里, 这下 try 不了了. 而 spring 是针对 java 编写的, 一旦反序列化器(默认为 jackson)抛出异常, 解码 HTTP 数据这整一步就会抛出 DecodingException 来. 更要命的是这一异常还会被包装成 ServerWebInputException , 内含硬编码写死的 message: "Failed to read HTTP message". 最终的 ErrorAttribute 无论到底是哪个字段为空了, 都是一模一样的:

{
  "timestamp": "xxx",
  "path": "xxx",
  "status": 400,
  "error": "Bad Request",
  "message": "Failed to read HTTP message",
  "requestId": "xxx"
}

毕竟对 java 而言, 如果反序列化失败了, 一定是 json 语法错误. spring 从未考虑过还会有别的情况会导致反序列化库抛出异常.

为了解决这个问题, 我们需要将 MissingKotlinParameterException (这是来自 jackson 的异常, 如果使用别的序列化库请用别的异常作为捕获条件)里包含的实际 message 提取出来作为最终的异常的 message.

@RestControllerAdvice
class JacksonMissingKotlinParameterControllerAdvice() {
    @ExceptionHandler(MissingKotlinParameterException::class)
    fun missingKotlinParameterAdvice(exception: Exception) {
        val actualException = exception.cause?.cause
        if (exception is ServerWebInputException && actualException is MissingKotlinParameterException) {
            throw ServerWebInputException(
                actualException.message ?: exception.reason,
                exception.methodParameter,
                exception.cause
            )
        } else {
            throw exception
        }
    }
}

实际上的异常堆栈是这样的: ServerWebInputException -> DecodingException -> MissingKotlinParameterException, 所以 cause 要跨两层. 而不符合这一结构和类型的异常就不是目标异常, 直接重新抛出它. 对于目标异常, 由于 ServerWebInputException 内部的字段不可修改, 所以创建一个新的, 除了 message 之外全部拷贝过去, message 用 MissingKotlinParameterException 里的 message, 就可以让最终返回给客户端的 message 包含到底是哪个字段错了的信息(在 ControllerAdvice 里抛出的异常将通过正常异常处理流程重新处理一遍):

{
  "timestamp": "xxx",
  "path": "xxx",
  "status": 400,
  "error": "Bad Request",
  "message": "Instantiation of [simple type, class xxx] value failed for JSON property xxx due to missing (therefore NULL) value for creator parameter xxx which is a non-nullable type\n at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 2] (through reference chain: xxx[\"xxx\"])",
  "requestId": "xxx"
}

虽然这样的 message 可能还需要进一步处理, 但是至少不是无论哪个字段错了都返回一样的消息了.