You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 04:50:25