PHP REST API 无法与基于Retrofit/Gson的Android应用POST请求正常对接
对接故障背景
- 目标功能:自研REST API接收安卓应用通过Retrofit/Gson发起的POST JSON请求,解析后执行SQL查询,返回格式化JSON响应。
- 已验证正常的环节:PHP端硬编码传入JSON、硬编码返回JSON时,所有API逻辑、SQL执行流程均可正常运行。
- 故障表现:
- 从应用侧通过POST传输JSON时,安卓端抛出致命错误:
FATAL EXCEPTION: DefaultDispatcher-worker-1或FATAL EXCEPTION: DefaultDispatcher-worker-2 - PHP端尝试3种JSON接收方案均未生效:
$jsonobj = http_get_request_body();$jsonobj = file_get_contents('php://input');$jsonobj = $_POST['code'];—— 不确定字段名规范,尝试替换为['text']等字段名均无返回
- 现有PHP脚本结构:
//database config here. if($_SERVER["REQUEST_METHOD"] == "POST"){ //method of reciving JSON here. $obj = json_decode($jsonobj); //SQL logic here. echo json_encode($return); }
- 从应用侧通过POST传输JSON时,安卓端抛出致命错误:
- 安卓端现有代码片段:
- API接口定义:
interface API { @POST("posttest.php") suspend fun postit(@Body post: String): Phone } - 接口调用逻辑:
val api = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(API::class.java) GlobalScope.launch(Dispatchers.IO) { val response = api.postit(test) try { //业务逻辑 } catch (e: Exception) { //错误处理 } } - 当前硬编码测试数据(原计划从Room读取数据,暂未实现):
private var test = "[\"text1\", \"text2\", \"text3\"]"
- API接口定义:
故障定位
根本原因1:Retrofit请求序列化逻辑错误
- 搭配默认
GsonConverterFactory使用@Body注解传String类型参数时,Gson会对传入的字符串执行二次序列化:原本传入的合法JSON数组字符串["text1","text2","text3"],会被转义为嵌套格式的字符串报文"[\"text1\", \"text2\", \"text3\"]"(外层包裹双引号、内部双引号被转义)。PHP端读到这个转义后的字符串后,json_decode解析失败得到null,后续操作null值直接抛出运行时错误。 - 关于
$_POST的使用误区:$_POST仅能解析application/x-www-form-urlencoded格式的表单POST参数,Retrofit通过@Body发送的是application/json格式的原始报文,不会填充到$_POST数组中,无论替换什么字段名都无法读取到值。
根本原因2:协程异常捕获范围错误
- 安卓端的try-catch仅包裹了请求成功后的业务逻辑,网络请求调用、响应解析阶段抛出的异常(如PHP端返回报错文本而非合法JSON)未被捕获,直接触发DefaultDispatcher的未捕获致命异常,即日志中看到的
FATAL EXCEPTION: DefaultDispatcher-worker-*。
修复方案
安卓端调整
- 避免手动传递JSON字符串被二次序列化:
- 方案一:定义与传输结构匹配的数据类,直接传对象给Retrofit,由Gson自动完成序列化:
// 对应传输结构的数据类 class TestRequest(val data: List<String> = listOf("text1", "text2", "text3")) // 接口定义修改为 interface API { @POST("posttest.php") suspend fun postit(@Body post: TestRequest): Phone } - 方案二:若必须传递原始JSON字符串,需手动指定请求Content-Type为
application/json,跳过Gson对字符串的序列化处理,避免报文被转义。
- 方案一:定义与传输结构匹配的数据类,直接传对象给Retrofit,由Gson自动完成序列化:
- 扩大协程异常捕获范围,覆盖全流程网络请求逻辑:
GlobalScope.launch(Dispatchers.IO) { try { val response = api.postit(test) // 后续业务逻辑 } catch (e: Exception) { // 错误处理,打印堆栈定位具体报错 } }
PHP端调整
- 统一使用
file_get_contents('php://input')读取原始请求报文,替换原有接收方式:if($_SERVER["REQUEST_METHOD"] == "POST"){ // 读取原始JSON请求体 $jsonobj = file_get_contents('php://input'); $obj = json_decode($jsonobj, true); // 增加解析失败校验,避免空值触发后续逻辑报错 if (is_null($obj)) { echo json_encode(["code" => 400, "msg" => "invalid request json"]); exit; } //SQL logic here. echo json_encode($return); } - 增加调试日志:将读取到的原始
php://input内容写入本地日志文件,确认实际收到的报文格式,避免盲目排查。
内容的提问来源于stack exchange,提问作者m.seigmiller
相关产品推荐
相关产品推荐

