同一份JSON的POST请求在Reqbin成功但Kotlin移动端失败求助
问题背景
使用Java/Kotlin开发移动端Ping功能,将Ping数据以JSON格式发送至Azure Web App,后者接收后存入SQL数据库。当前遇到的问题是:移动端发送的JSON payload在Web App端无法解析,Azure日志流抛出Json input formatter异常;但用reqbin.com发送完全相同的JSON时请求成功,数据可正常存入数据库,无法定位问题点。
相关代码与信息
1. 构造JSON payload的Kotlin代码
// 导入必要依赖 fun createJsonPing( timeOfPing: LocalDateTime, deviceId: String, smartphoneId: String, note: String ): String { val messageObject = JSONObject() try { messageObject.put("TimeOfPing", formatDateTimeToISO(timeOfPing)) messageObject.put("SmartphoneId", smartphoneId) messageObject.put("DeviceId", deviceId) messageObject.put("Note", note) } catch (e: Exception) { e.printStackTrace() throw JSONException(e.message ?: "Unknown error") Log.i("createJsonPing", "Failed to construct Json ping") } val jsonMessageStringPing = messageObject.toString() Log.i("createPing", "Ping: \n" + jsonMessageStringPing) Log.i("createPing", "Size of ping is: ${jsonMessageStringPing.toByteArray(Charsets.UTF_8).size} b") return jsonMessageStringPing } fun formatDateTimeToISO(dateTime: LocalDateTime): String { val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS") return dateTime.format(formatter) }
2. Ktor发送请求代码
// 初始化客户端 private val client = HttpClient(CIO) { install(Logging) { level = LogLevel.ALL } install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true prettyPrint = true //isLenient = true }) } } suspend fun simpleSend() { try { val response: HttpResponse = client.post("https://myazurewebapp.azurewebsites.net/ping") { headers { append(HttpHeaders.Accept, "application/json") append(HttpHeaders.ContentType, "application/json") } //contentType(ContentType.Application.Json) // 也曾尝试使用此行 setBody(createJsonPing( LocalDateTime.now(), "TEST", "test", "testing ping")) }.body() Log.i(TAG, "HTTP Response: $response") } catch (e: Exception) { Log.i(TAG, "Request failed: $e") } finally { close() } }
3. 发送的JSON示例
{"TimeOfPing":"2024-06-21T14:38:00.121", "SmartphoneId":"TEST", "DeviceId":"test", "Note":"testing ping"}
4. Azure日志流关键错误信息
2024-06-21 12:20:50.901 +00:00 [Debug] Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter: JSON input formatter threw an exception: The JSON value could not be converted to api.Dtos.MessagePing.CreateMessagePingRequestDto. Path: $ | LineNumber: 0 | BytePositionInLine: 127.
5. Web App接收请求的CreateMessagePingRequestDto结构
{ "type": "object", "properties": { "TimeOfPing": { "type": "string" }, "SmartphoneId": { "type": "string" }, "DeviceId": { "type": "string" }, "Note": { "type": "string" } }, "additionalProperties": false }
排查思路与解决方案
核心问题分析
手动构造JSON字符串并通过Ktor发送时,Ktor的ContentNegotiation组件会将String类型的body当作纯文本处理,而非JSON对象。即使设置了Content-Type: application/json,后端收到的可能是被转义的字符串(例如原JSON被包裹在双引号中,内部引号转义),导致后端JSON解析器无法将其转换为目标DTO。
解决方案:改用数据类自动序列化
- 定义对应的数据类
data class PingRequest( val TimeOfPing: String, val SmartphoneId: String, val DeviceId: String, val Note: String )
- 修改发送逻辑,直接传入数据类
suspend fun simpleSend() { try { val pingRequest = PingRequest( TimeOfPing = formatDateTimeToISO(LocalDateTime.now()), SmartphoneId = "TEST", DeviceId = "test", Note = "testing ping" ) val response = client.post("https://myazurewebapp.azurewebsites.net/ping") { contentType(ContentType.Application.Json) setBody(pingRequest) // 直接传入数据类,由ContentNegotiation自动序列化 } Log.i(TAG, "HTTP Response Status: ${response.status}") } catch (e: Exception) { Log.i(TAG, "Request failed: $e") } finally { client.close() } }
额外排查点
- 抓包验证实际请求体:使用Charles或Wireshark抓取移动端发送的请求,确认body是否与日志中打印的JSON一致,是否存在转义或编码问题。
- 后端日期格式兼容:如果后端
TimeOfPing字段是DateTime类型而非字符串,需确保后端JSON解析器支持yyyy-MM-dd'T'HH:mm:ss.SSS格式(可在后端配置JsonSerializerOptions指定日期格式)。 - 字符编码一致性:确保移动端发送和后端接收均使用UTF-8编码,避免因编码差异导致的解析失败。
内容的提问来源于stack exchange,提问作者koralröv

