a
This commit is contained in:
315
服务器响应警告问题分析.txt
Normal file
315
服务器响应警告问题分析.txt
Normal file
@@ -0,0 +1,315 @@
|
||||
# 服务器响应警告问题分析
|
||||
|
||||
## 问题描述
|
||||
|
||||
从日志中可以看到,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接口
|
||||
**严重程度**: 中等(功能正常,但响应格式不规范)
|
||||
|
||||
Reference in New Issue
Block a user