Flutter日程应用从Google Sheets恢复备份数据时JSON解析失败求助
问题排查与修复方案
核心问题
恢复数据时出现Expected JSON but got text/html错误,根源在于两个关键点:
- 前端GET请求参数传递方式错误:你通过请求头传递
acc和tbl,但Google Apps Script的doGet方法仅解析URL查询参数,无法识别请求头中的参数,导致后端返回HTML错误页面。 - 后端(Google Apps Script)未正确返回JSON格式数据:即使参数传递正确,若后端未明确设置返回MIME类型为JSON,也会导致Content-Type不符合预期。
修复步骤
1. 修改前端GET请求参数传递方式
将acc和tbl从请求头移到URL查询参数中,修改BackendHelper的getData方法:
import 'package:http/http.dart' as http; import 'package:logger/logger.dart'; class BackendHelper { static const String _server = "script.google.com"; static Logger logger = Logger(); // 保留sendData方法不变 static Future<int> sendData(String jsonData) async { var client = http.Client(); int code = -1; try { var response = await client.post( Uri.https(_server, "macros/s/AKfycby3g-CRk53h4Rr07VWxJr4d6CYm33_-cft8nOeIlwAuOFwAjzMz6jDamo_HHxpFtTz-/exec"), body: {'acc': '2', 'tbl': 'Contacts', 'data': jsonData}, ); code = response.statusCode; logger.i(response.statusCode); } finally { client.close(); } return code; } static Future<String> getData() async { var client = http.Client(); String responseBody = ''; try { var response = await client.get( Uri.https( _server, "macros/s/AKfycby3g-CRk53h4Rr07VWxJr4d6CYm33_-cft8nOeIlwAuOFwAjzMz6jDamo_HHxpFtTz-/exec", {'acc': '1', 'tbl': 'Contacts'}, // 参数移至查询参数中 ), ); if (response.statusCode == 200) { if (response.headers['content-type']?.contains('application/json') == true) { responseBody = response.body; logger.i('Data retrieved successfully'); } else { logger.e('Expected JSON but got ${response.headers['content-type']}'); // 打印原始响应内容,方便排查后端问题 logger.d('Raw response body: ${response.body}'); responseBody = 'Invalid response: Expected JSON'; } } else { logger.e('Failed to retrieve data. Status code: ${response.statusCode}'); logger.d('Raw response body: ${response.body}'); responseBody = 'Error: ${response.statusCode}'; } } finally { client.close(); } return responseBody; } }
2. 确保Google Apps Script后端正确返回JSON
以下是标准的doGet实现,确保它能处理查询参数并返回正确的JSON:
function doGet(e) { // 从查询参数中获取acc和tbl const acc = e.parameter.acc; const tbl = e.parameter.tbl; // 处理获取联系人的请求 if (acc === '1' && tbl === 'Contacts') { try { // 获取对应工作表数据 const spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); const sheet = spreadsheet.getSheetByName('Contacts'); if (!sheet) { throw new Error('Sheet "Contacts" not found'); } // 转换表头+数据为JSON格式(跳过第一行表头) const [headers, ...rows] = sheet.getDataRange().getValues(); const contacts = rows.map(row => { const contact = {}; headers.forEach((header, index) => { contact[header] = row[index]; }); return contact; }); // 返回JSON数据,明确设置MIME类型 return ContentService.createTextOutput(JSON.stringify(contacts)) .setMimeType(ContentService.MimeType.JSON); } catch (error) { // 错误时返回JSON格式的错误信息 return ContentService.createTextOutput(JSON.stringify({error: error.message})) .setMimeType(ContentService.MimeType.JSON); } } else { // 无效请求返回JSON错误 return ContentService.createTextOutput(JSON.stringify({error: 'Invalid request parameters'})) .setMimeType(ContentService.MimeType.JSON); } }
3. 额外排查步骤
- 直接测试后端接口:在浏览器中访问以下URL,查看返回内容是否为JSON:
如果返回HTML,说明后端逻辑存在问题(比如权限未设置、工作表名称错误等);如果返回JSON,再检查前端请求。https://script.google.com/macros/s/AKfycby3g-CRk53h4Rr07VWxJr4d6CYm33_-cft8nOeIlwAuOFwAjzMz6jDamo_HHxpFtTz-/exec?acc=1&tbl=Contacts - 检查GAS权限:确保Google Apps Script已部署为“任何人,甚至匿名”可访问(部署时设置权限),否则前端请求会被拦截返回权限错误的HTML。
验证修复
修改完成后重新测试恢复功能:
- 后端返回的Content-Type会变为
application/json - 前端能正确解析JSON数据并写入本地数据库
- 恢复成功后会显示对应的SnackBar提示
内容的提问来源于stack exchange,提问作者SERGIO ANDRES VALDERRAMA VELEZ
相关产品推荐
相关产品推荐

