本文记录使用Ktor Client调用微信小程序生成二维码接口时遇到的412 Precondition Failed问题及解决方案。


问题现象

使用Ktor Client的post方法请求微信生成小程序二维码接口时,服务端返回错误:

HTTP/1.1 412 Precondition Failed

请求头检查发现缺失Content-Length头,但代码中已明确设置请求体。


问题定位

关键原因 微信接口对Content-Length请求头有强制校验,而Ktor Client默认配置(使用CIO引擎+Jackson序列化)会启用streamRequestBody=true。这会采用流式传输导致:

  1. 自动省略Content-Length

  2. 使用Transfer-Encoding: chunked

验证方式 通过抓包工具(如Wireshark)可见实际请求头:

POST /wxa/getwxacodeunlimit?access_token=TOKEN HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: api.weixin.qq.com
Transfer-Encoding: chunked

解决方案

方案一:手动序列化

实现步骤

  1. 使用Jackson手动序列化对象为JSON字符串

  2. 指定Content-Typeapplication/json

val httpClient = HttpClient(CIO) {
    install(ContentNegotiation) {
        jackson()
    }
}
​
suspend fun generateQrCode() {
    val requestBody = mapOf(
        "path" to "pages/index/index",
        "scene" to "id=1"
    )
    
    // 手动序列化为JSON字符串
    val jsonBody = JsonObjectMapper().writeValueAsString(requestBody)
    
    val response: HttpResponse = httpClient.post("https://api.weixin.qq.com/wxa/getwxacodeunlimit") {
        url {
            parameter("access_token", accessToken)
        }
        contentType(ContentType.Application.Json)
        setBody(jsonBody)
    }
    
    // 处理响应...
}

方案二:禁用流式请求体

实现步骤

  1. 配置Jackson的streamRequestBodyfalse

  2. Ktor自动计算并添加Content-Length

val httpClient = HttpClient(CIO) {
    install(ContentNegotiation) {
        jackson(contentType = ContentType.Application.Json, streamRequestBody = false) {// 关键配置:禁用流式传输
            registerModules(KotlinModule.Builder().build())
            configure(SerializationFeature.INDENT_OUTPUT, false)
            setSerializationInclusion(JsonInclude.Include.NON_NULL)
        }
    }
}
​
suspend fun generateQrCode() {
    val requestBody = mapOf(
        "path" to "pages/index/index",
        "scene" to "id=1"
    )
    
    val response: HttpResponse = httpClient.post("https://api.weixin.qq.com/wxa/getwxacodeunlimit") {
        url {
            parameter("access_token", accessToken)
        }
        contentType(ContentType.Application.Json)
        setBody(requestBody)
    }
    
    // 处理响应...
}

总结

  1. HTTP规范注意:对接严格遵循HTTP标准的接口时,要特别注意请求头完整性

  2. Ktor特性:streamRequestBody=true适用于大文件传输,但会改变头信息策略

  3. 灵活选择:根据场景选择手动控制序列化或全局配置,小程序接口推荐方案二

建议在微信生态开发时,使用apifox等工具先验证请求头格式,再移植到代码实现。