Dio请求遇FormatException错误,Postman正常请求求排查方案
问题:Dio请求抛出FormatException异常,Postman请求正常
使用Dio发起网络请求时遇到以下错误:
DioError [DioErrorType.other]: FormatException: Unexpected character (at line 9, character 1)
已确认Postman发送相同参数可正常获取响应,但修改Model的JSON解析方式后问题仍未解决,需分析原因并给出解决方案。
相关代码实现
DioHelper类
class DioHelper { static late Dio dio; static init() { dio = Dio(BaseOptions( baseUrl: "https://management.rawcodev.com/api/", receiveDataWhenStatusError: true, // 避免抛出Http status error 422这类响应错误 validateStatus: (status) => true, )); } static Future<Response> getData({ required String path, Map<String, dynamic>? query, String lang = "en", String? token, }) async { dio.options.headers = { "lang": lang, "Authorization": token, "Content-Type": "application/json" }; return await dio.get(path, queryParameters: query); } }
Cubit中获取数据的方法
Attendance_History_Model? attendHistoryModel; void getAttendHistory() { emit(AttendanceHistoryLoding()); DioHelper.getData(path: AttendHistory, query: { "mod": "attendance-history", "key": "ABC@123", "user_id": user_idconst, "device_id": "device-1" }).then((value) { if (value.statusCode == 200) { attendHistoryModel = Attendance_History_Model.fromJson(value.data); emit(AttendanceHistorySuccess()); } else { print("error"); print(value.statusCode); } }).catchError((error) { print(error.toString()); emit(AttendanceHistoryError()); }); }
问题原因分析
- JSON解析失败:FormatException本质是Dio尝试自动将响应解析为JSON时失败,可能是响应内容并非标准JSON格式(比如包含隐藏字符、HTML错误页面、编码不匹配)。
- Content-Type不匹配:若服务器返回的
Content-Type响应头不是application/json,Dio的自动JSON解析逻辑会出错,而Postman会自动识别响应格式并处理。 - 响应编码问题:服务器返回的响应编码不是UTF-8(比如GBK),Dio默认用UTF-8解析会导致字符解析异常。
- 异常响应未被捕获:由于
validateStatus: (status) => true,所有状态码的响应都会被返回,若服务器在非200状态下返回非JSON内容(如HTML错误页),也会触发解析错误。
解决方案
1. 先排查原始响应内容
在Cubit的请求回调中打印原始响应内容,确认是否为合法JSON:
DioHelper.getData(...).then((value) { // 打印原始响应字符串,检查是否有异常字符或非JSON内容 print("原始响应内容: ${value.data}"); print("响应头Content-Type: ${value.headers['content-type']}"); if (value.statusCode == 200) { try { // 手动解析JSON并捕获异常 var jsonData = jsonDecode(value.data); attendHistoryModel = Attendance_History_Model.fromJson(jsonData); emit(AttendanceHistorySuccess()); } catch (e) { print("Model解析失败: $e"); } } else { print("请求状态码异常: ${value.statusCode}"); } })
2. 关闭Dio自动JSON解析,手动处理
修改Dio配置,将响应类型设为原始字符串,再手动解析JSON,避免自动解析失败抛出异常:
static Future<Response> getData({ required String path, Map<String, dynamic>? query, String lang = "en", String? token, }) async { dio.options.headers = { "lang": lang, "Authorization": token, "Content-Type": "application/json" }; // 设置响应类型为原始字符串,关闭自动JSON解析 dio.options.responseType = ResponseType.plain; Response response = await dio.get(path, queryParameters: query); // 手动尝试解析JSON try { response.data = jsonDecode(response.data); } catch (e) { print("JSON解析失败: $e"); print("原始响应内容: ${response.data}"); } return response; }
3. 处理响应编码问题
若服务器返回非UTF-8编码的响应,可自定义响应解码器(需引入charset_converter包处理非UTF-8编码):
static init() { dio = Dio(BaseOptions( baseUrl: "https://management.rawcodev.com/api/", receiveDataWhenStatusError: true, validateStatus: (status) => true, // 自定义解码器,处理GBK编码示例 responseDecoder: (List<int> bytes, RequestOptions options, ResponseBody responseBody) { String data = gbk.decode(bytes); // 若为GBK编码 return jsonDecode(data); }, )); }
4. 过滤异常响应
即使设置了validateStatus: true,也可在回调中先判断响应是否为JSON格式,再进行解析:
if (value.statusCode == 200) { // 先判断响应是否为JSON格式 if (value.headers['content-type']?.contains('application/json') == true) { try { attendHistoryModel = Attendance_History_Model.fromJson(value.data); emit(AttendanceHistorySuccess()); } catch (e) { print("Model解析失败: $e"); } } else { print("响应非JSON格式: ${value.headers['content-type']}"); } }
内容的提问来源于stack exchange,提问作者Amal yosri
相关产品推荐
相关产品推荐

