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

Flutter+Chopper请求报错:_Map<String,dynamic>非Auth?子类型

问题:POST请求返回200,但拦截器抛出TypeError异常(仅成功请求触发)

发起POST请求时,服务器返回200状态码,但拦截器抛出TypeError异常。仅成功请求会出现该问题,失败请求可正常返回Object?,需排查问题。

Chopper Client

class ClientName {
  @pragma('Singleton')
  ClientName._();
  static final instance = ClientName._();

  @pragma('Client')
  final ChopperClient client = ChopperClient(
      converter: const JsonConverter(),
      errorConverter: const JsonConverter(),
      baseUrl: Uri(scheme: 'https', host: 'redacted'),
      services: [AuthService.create()]);

  @pragma('Services')
  AuthService get auth => client.getService<AuthService>();
}

Auth Service

// 生成器运行必需代码
part "auth_service.chopper.dart";

@ChopperApi()
abstract class AuthService extends ChopperService {
  static AuthService create([ChopperClient? client]) => _$AuthService(client);

  @Post(path: "/v1/oauth/token", headers: {"Accept": "application/json"})
  Future<Response<Auth>> login(@Body() Map<String, dynamic> data);
}

Auth Model

class Auth {
  Auth(
    this.status,
    this.currentTime,
    this.accessToken,
    this.tokenType,
    this.userId,
    this.expiresAt,
    this.createdAt,
  );
  final String status;
  final String currentTime;
  final String accessToken;
  final String tokenType;
  final int userId;
  final String expiresAt;
  final String createdAt;

  Auth copyWith({
    String? status,
    String? currentTime,
    String? accessToken,
    String? tokenType,
    int? userId,
    String? expiresAt,
    String? createdAt,
  }) {
    return Auth(
      status ?? this.status,
      currentTime ?? this.currentTime,
      accessToken ?? this.accessToken,
      tokenType ?? this.tokenType,
      userId ?? this.userId,
      expiresAt ?? this.expiresAt,
      createdAt ?? this.createdAt,
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'status': status,
      'currentTime': currentTime,
      'accessToken': accessToken,
      'tokenType': tokenType,
      'userId': userId,
      'expiresAt': expiresAt,
      'createdAt': createdAt,
    };
  }

  factory Auth.fromMap(Map<String, dynamic> map) {
    return Auth(
      map['status'] as String,
      map['currentTime'] as String,
      map['accessToken'] as String,
      map['tokenType'] as String,
      map['userId'] as int,
      map['expiresAt'] as String,
      map['createdAt'] as String,
    );
  }

  String toJson() => json.encode(toMap());

  factory Auth.fromJson(String source) => Auth.fromMap(json.decode(source) as Map<String, dynamic>);

  @override
  String toString() {
    return 'Auth(status: $status, currentTime: $currentTime, accessToken: $accessToken, tokenType: $tokenType, userId: $userId, expiresAt: $expiresAt, createdAt: $createdAt)';
  }

  @override
  bool operator ==(covariant Auth other) {
    if (identical(this, other)) return true;

    return other.status == status &&
        other.currentTime == currentTime &&
        other.accessToken == accessToken &&
        other.tokenType == tokenType &&
        other.userId == userId &&
        other.expiresAt == expiresAt &&
        other.createdAt == createdAt;
  }

  @override
  int get hashCode {
    return status.hashCode ^
        currentTime.hashCode ^
        accessToken.hashCode ^
        tokenType.hashCode ^
        userId.hashCode ^
        expiresAt.hashCode ^
        createdAt.hashCode;
  }
}

请求代码

class LoginRepository extends Repository {
  Future<Response<Auth>> login(String username, String password) async {
    return ClientName.instance.auth.login(AuthRequest(grantType: "password", username: username, password: password).toMap());
  }
}

排查与解决方法

  1. 自定义Converter处理模型解析
    默认JsonConverter无法自动识别Auth类的fromMap方法,需自定义转换器完成JSON到模型的映射:
class CustomJsonConverter extends JsonConverter {
  @override
  Response<ResultType> convertResponse<ResultType, Item>(Response response) {
    final baseResponse = super.convertResponse(response);
    if (ResultType == Auth) {
      return Response(
        body: Auth.fromMap(baseResponse.body) as ResultType,
        statusCode: baseResponse.statusCode,
        headers: baseResponse.headers,
        error: baseResponse.error,
      );
    }
    return baseResponse;
  }
}

替换ChopperClient中的converter: const JsonConverter()为converter: CustomJsonConverter()。

  1. 修复响应字段不匹配与强制转换风险
    检查服务器返回的JSON字段名、类型是否与Auth类完全一致(注意大小写)。同时避免强制转换导致崩溃,修改fromMap方法添加空安全兜底:
factory Auth.fromMap(Map<String, dynamic> map) {
  return Auth(
    map['status'] as String? ?? '',
    map['currentTime'] as String? ?? '',
    map['accessToken'] as String? ?? '',
    map['tokenType'] as String? ?? '',
    map['userId'] as int? ?? 0,
    map['expiresAt'] as String? ?? '',
    map['createdAt'] as String? ?? '',
  );
}
  1. 重新生成Chopper代码
    确保生成的auth_service.chopper.dart文件正确,执行以下命令重新生成:
dart run build_runner build

内容的提问来源于stack exchange,提问作者Brandon Stillitano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 21:40:59