Flutter GetConnect认证请求报错:Null无法转为Response<AuthResponse>
Flutter GetConnect 类型转换错误排查:'Null' is not a subtype of type 'Response'
错误信息
Unhandled Exception: type 'Null' is not a subtype of type 'Response
' in type cast
相关代码实现
AuthService类
class AuthService { final IHttpConnect _connect; const AuthService(IHttpConnect connect) : _connect = connect; String get _prefix => 'auth'; Future<AuthResponse> authenticateUser( var body, ) async { final response = await _connect.post( '$_prefix/login', body, decoder: (value){ print(value); AuthResponse data = AuthResponse.fromJson( value as Map<String, dynamic>, ); return data; }, ); if (response.success) { return response.payload!; } else { switch (response.statusCode) { case 404: throw UserNotFoundException(); default: throw DefaultException(message: response.payload!.error!); } } } }
AuthResponse模型
import 'package:json_annotation/json_annotation.dart'; part 'auth_response.g.dart'; @JsonSerializable() class AuthResponse { AuthResponse({ required this.success, this.data, this.error, }); final bool success; final Data? data; final String? error; factory AuthResponse.fromJson(Map<String, dynamic> json) => _$AuthResponseFromJson(json); Map<String, dynamic> toJson() => _$AuthResponseToJson(this); } @JsonSerializable() class Data { Data({ required this.token, required this.user, }); final String token; final User user; factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json); Map<String, dynamic> toJson() => _$DataToJson(this); } @JsonSerializable() class User { User({ required this.id, required this.name, required this.email, required this.emailVerifiedAt, required this.createdAt, required this.updatedAt, }); final int id; final String name; final String email; @JsonKey(name: 'email_verified_at') final DateTime? emailVerifiedAt; @JsonKey(name: 'created_at') final DateTime createdAt; @JsonKey(name: 'updated_at') final DateTime updatedAt; factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); }
IHttpConnect抽象类
import './response.model.dart'; abstract class IHttpConnect { Future<Response<T>> get<T>( String url, { required T Function(dynamic)? decoder, }); Future<Response<T>> post<T>( String url, Map<String, dynamic> body, { T Function(dynamic)? decoder, }); Future<Response<T>> put<T>( String url, Map<String, dynamic> body, { T Function(dynamic)? decoder, }); Future<Response<T>> patch<T>( String url, Map<String, dynamic> body, { T Function(dynamic)? decoder, }); Future<Response<T>> delete<T>( String url, { required T Function(dynamic)? decoder, }); }
Response类
class Response<T> { final int statusCode; final T? payload; bool get success => statusCode <= 200; const Response({ required this.statusCode, required this.payload, }); }
API响应数据
{success: true, data: {token: 157|4YXMdrMwDIECxsVSf5hIeON5scCu9lZTQP2B5wXa, user: {id: 1, name: Kenya Friesen, email: mdavis@yahoo.com, email_verified_at: 2022-11-18T00:27:42.000000Z, created_at: 2022-11-18T00:27:42.000000Z, updated_at: 2022-11-18T00:27:42.000000Z}}, error: ddd}
错误原因分析
- IHttpConnect实现类未正确封装GetConnect响应:自定义的
IHttpConnect如果没有正确实现post方法,导致返回Null,就会触发类型转换错误。GetConnect自身的post返回的是Getx的Response,必须手动映射到你自定义的Response类。 - 泛型类型未明确指定:调用
_connect.post时未明确泛型<AuthResponse>,类型推断可能出错,导致返回的响应类型不匹配。 - success判断逻辑错误:自定义
Response的success用statusCode <= 200,不符合HTTP标准(成功状态码是200-299),会导致正常响应被误判为失败,后续强制解包payload引发问题。 - 空安全处理不严谨:直接使用
!强制解包payload,如果payload为空会触发空指针,同时加剧类型转换错误的影响。
解决方案
1. 实现正确的IHttpConnect封装类
确保将GetConnect的响应正确映射到自定义Response,避免返回Null:
class HttpConnectImpl extends GetConnect implements IHttpConnect { @override Future<Response<T>> get<T>(String url, {required T Function(dynamic)? decoder}) async { final getxResponse = await super.get(url, decoder: decoder); return Response<T>( statusCode: getxResponse.statusCode ?? 500, payload: getxResponse.body, ); } @override Future<Response<T>> post<T>(String url, Map<String, dynamic> body, {T Function(dynamic)? decoder}) async { final getxResponse = await super.post(url, body, decoder: decoder); return Response<T>( statusCode: getxResponse.statusCode ?? 500, payload: getxResponse.body, ); } // 实现put/patch/delete方法,逻辑与post一致 }
2. 明确指定泛型类型并修正body类型
调用post时明确泛型,同时将body的类型从var改为Map<String, dynamic>,避免类型模糊:
Future<AuthResponse> authenticateUser( Map<String, dynamic> body, // 修正参数类型 ) async { final response = await _connect.post<AuthResponse>( // 明确指定泛型 '$_prefix/login', body, decoder: (value){ print(value); return AuthResponse.fromJson(value as Map<String, dynamic>); }, ); // ... 后续逻辑 }
3. 修正success判断逻辑
改为符合HTTP标准的成功状态码范围:
class Response<T> { final int statusCode; final T? payload; // 修正success判断 bool get success => statusCode >= 200 && statusCode < 300; const Response({ required this.statusCode, required this.payload, }); }
4. 优化空安全处理
避免强制解包,增加空判断:
if (response.success) { return response.payload ?? throw Exception("响应数据不能为空"); } else { switch (response.statusCode) { case 404: throw UserNotFoundException(); default: final errorMessage = response.payload?.error ?? "未知错误"; throw DefaultException(message: errorMessage); } }
内容的提问来源于stack exchange,提问作者Brian Mutiso
相关产品推荐
相关产品推荐

