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

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 最新话题
Help Net Security
Help Net Security
N
News | PayPal Newsroom
www.infosecurity-magazine.com
www.infosecurity-magazine.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The Last Watchdog
The Last Watchdog
S
Security @ Cisco Blogs
W
WeLiveSecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
Webroot Blog
Webroot Blog
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
N
News and Events Feed by Topic
T
Threat Research - Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tor Project blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
T
Tailwind CSS Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
S
SegmentFault 最新的问题
J
Java Code Geeks
P
Privacy & Cybersecurity Law Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 【当耐特】
博客园_首页
H
Hacker News: Front Page
T
Threatpost
Jina AI
Jina AI
博客园 - Franky
月光博客
月光博客
L
LINUX DO - 热门话题
The Cloudflare Blog
H
Heimdal Security Blog
博客园 - 司徒正美
酷 壳 – CoolShell
酷 壳 – CoolShell
Cloudbric
Cloudbric
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
S
Secure Thoughts
T
Tenable Blog
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

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 可能还需要进一步处理, 但是至少不是无论哪个字段错了都返回一样的消息了.