Files
HouseProperty/服务器响应警告问题分析.txt
renjianbo 39f1da9ca0 a
2026-01-09 16:09:42 +08:00

316 lines
9.0 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 服务器响应警告问题分析
## 问题描述
从日志中可以看到API响应中混入了PHP警告信息
```
<br />
<b>Warning</b>: fopen(/var/www/wy/log.dat): failed to open stream: No such file or directory in <b>/home/renjianbo/saars/wy/wy/wy/server/application/Interface/libraries/Api/Goods/AddGoodsInfo.php</b> on line <b>85</b><br />
<br />
<b>Warning</b>: fwrite() expects parameter 1 to be resource, bool given in <b>/home/renjianbo/saars/wy/wy/wy/server/application/Interface/libraries/Api/Goods/AddGoodsInfo.php</b> on line <b>86</b><br />
<br />
<b>Warning</b>: fclose() expects parameter 1 to be resource, bool given in <b>/home/renjianbo/saars/wy/wy/wy/server/application/Interface/libraries/Api/Goods/AddGoodsInfo.php</b> on line <b>87</b><br />
{
"status": 0,
"msg": "添加成功",
"data": {...}
}
```
## 问题原因
### 服务器端问题
1. **日志文件路径不存在**
- 服务器尝试打开日志文件:`/var/www/wy/log.dat`
- 文件或目录不存在,导致 `fopen()` 返回 `false`
2. **错误处理不当**
- PHP代码没有检查 `fopen()` 的返回值
- 直接对 `false` 值调用 `fwrite()` 和 `fclose()`
- PHP警告被输出到HTTP响应中
3. **错误输出配置**
- PHP的 `display_errors` 可能被设置为 `On`
- 导致警告信息输出到响应体中
### 影响分析
#### ✅ 当前状态
- **业务功能正常**虽然响应中有警告但JSON数据仍然成功解析
- **状态码正确**`status: 0` 表示操作成功
- **数据完整**`data` 字段包含完整的业务数据
#### ⚠️ 潜在风险
1. **JSON解析失败风险**
- 如果警告信息在JSON之前可能导致Gson解析失败
- 某些严格的JSON解析器可能无法处理混入HTML的响应
2. **响应体污染**
- 响应体包含非JSON内容增加解析复杂度
- 可能在某些情况下导致解析异常
3. **日志记录失败**
- 服务器端日志无法正常记录
- 影响问题排查和监控
## 解决方案
### 方案一:服务器端修复(推荐)
#### 1. 修复日志文件路径
在服务器端 `AddGoodsInfo.php` 文件中:
```php
// 修改前第85行
$logFile = fopen('/var/www/wy/log.dat', 'a');
// 修改后
$logDir = '/var/www/wy/';
$logFile = $logDir . 'log.dat';
// 确保目录存在
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
// 检查文件是否可写
$handle = @fopen($logFile, 'a');
if ($handle === false) {
// 记录到系统日志或使用error_log
error_log("无法打开日志文件: $logFile");
// 不输出警告到响应
} else {
// 正常写入日志
fwrite($handle, $logContent);
fclose($handle);
}
```
#### 2. 关闭错误输出
在PHP配置或代码中
```php
// 关闭错误显示(生产环境)
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php_errors.log');
```
#### 3. 使用异常处理
```php
try {
$logFile = fopen('/var/www/wy/log.dat', 'a');
if ($logFile === false) {
throw new Exception('无法打开日志文件');
}
fwrite($logFile, $logContent);
fclose($logFile);
} catch (Exception $e) {
// 记录到系统日志,不输出到响应
error_log($e->getMessage());
}
```
### 方案二:客户端容错处理(临时方案)
如果暂时无法修改服务器端,可以在客户端添加响应清理:
#### 1. 创建自定义Gson Converter
创建文件:`app/src/main/java/http/CleanResponseConverter.java`
```java
package http;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import java.io.IOException;
import java.nio.charset.Charset;
import okio.Buffer;
import okio.BufferedSource;
public class CleanResponseConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
CleanResponseConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public T convert(ResponseBody value) throws IOException {
BufferedSource bufferedSource = value.source();
bufferedSource.request(Long.MAX_VALUE);
Buffer buffer = bufferedSource.buffer();
String responseString = buffer.clone().readString(Charset.forName("UTF-8"));
// 清理PHP警告和HTML标签
responseString = cleanResponse(responseString);
try {
return adapter.fromJson(responseString);
} finally {
value.close();
}
}
/**
* 清理响应中的PHP警告和HTML标签
*/
private String cleanResponse(String response) {
if (response == null) {
return "";
}
// 移除PHP警告<br />和<b>Warning</b>标签)
response = response.replaceAll("<br\\s*/?>", "");
response = response.replaceAll("<b>Warning</b>", "");
response = response.replaceAll("</b>", "");
response = response.replaceAll("<b>", "");
// 查找JSON开始位置第一个{或[
int jsonStart = -1;
for (int i = 0; i < response.length(); i++) {
char c = response.charAt(i);
if (c == '{' || c == '[') {
jsonStart = i;
break;
}
}
// 如果找到JSON开始位置只保留JSON部分
if (jsonStart > 0) {
response = response.substring(jsonStart);
}
// 移除JSON结束后的所有内容
int jsonEnd = response.lastIndexOf('}');
if (jsonEnd > 0 && response.trim().endsWith("}")) {
// JSON以}结尾,保留
} else if (jsonEnd > 0) {
response = response.substring(0, jsonEnd + 1);
}
return response.trim();
}
}
```
#### 2. 创建Converter Factory
创建文件:`app/src/main/java/http/CleanResponseConverterFactory.java`
```java
package http;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import retrofit2.Converter;
import retrofit2.Converter.Factory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
public class CleanResponseConverterFactory extends Factory {
private final Gson gson;
public CleanResponseConverterFactory(Gson gson) {
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, retrofit2.Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new CleanResponseConverter<>(gson, (TypeAdapter<Object>) adapter);
}
}
```
#### 3. 修改RetrofitServiceManager
在 `app/src/main/java/http/RetrofitServiceManager.java` 中:
```java
// 修改前
.addConverterFactory(GsonConverterFactory.create())
// 修改后
.addConverterFactory(new CleanResponseConverterFactory(new Gson()))
```
## 问题定位
### 服务器端文件位置
- **文件路径**: `/home/renjianbo/saars/wy/wy/wy/server/application/Interface/libraries/Api/Goods/AddGoodsInfo.php`
- **问题行数**: 第85-87行
### 相关API接口
- **接口**: `AddGoodsInfo` (添加商品信息)
- **请求URL**: `http://101.43.95.130:8030/api/`
- **请求参数**:
- `app`: "Goods"
- `class`: "AddGoodsInfo"
- `sign`: MD5签名
## 测试验证
### 验证服务器端修复
1. 检查日志文件是否存在:`ls -la /var/www/wy/log.dat`
2. 检查目录权限:`ls -ld /var/www/wy/`
3. 测试API响应是否干净无PHP警告
### 验证客户端修复
1. 查看日志中响应体是否包含警告
2. 确认JSON解析是否正常
3. 测试业务功能是否正常
## 建议优先级
### 🔴 高优先级(立即处理)
1. **服务器端修复日志文件路径问题**
- 创建目录或修复路径
- 添加错误检查
2. **关闭PHP错误输出到响应**
- 设置 `display_errors = 0`
- 使用 `error_log` 记录错误
### 🟡 中优先级(短期处理)
1. **添加客户端响应清理**
- 实现自定义Converter
- 清理响应中的HTML标签
2. **完善错误处理机制**
- 添加响应验证
- 添加异常处理
### 🟢 低优先级(长期优化)
1. **统一日志记录机制**
- 使用统一的日志服务
- 添加日志轮转
2. **API响应标准化**
- 确保所有API响应格式统一
- 添加响应验证
## 总结
当前问题虽然不影响业务功能,但存在潜在风险。建议:
1. **优先修复服务器端**:从根本上解决问题
2. **客户端添加容错**:作为临时保护措施
3. **完善监控机制**:及时发现类似问题
---
**问题发现时间**: 2026-01-09 15:40:30
**影响范围**: AddGoodsInfo API接口
**严重程度**: 中等(功能正常,但响应格式不规范)