Flutter响应数据解析插件response_parser的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter响应数据解析插件response_parser的使用

简介

Response Parser 是一个用于简化从服务器解析数据和错误响应的Flutter插件。它可以帮助你编写更简洁、更易于维护的网络请求处理代码。

特性

  • 简化错误处理:自动处理不同类型的错误,并返回统一的错误对象。
  • 类型安全:使用 Either 类型来表示成功或失败的结果。
  • 灵活配置:可以通过自定义解析器来适应不同的API响应格式。

许可证与版本

入门

假设你希望编写如下的简洁函数:

Future<Either<ApiFailure, User>> fetchUser() async {
  return parseApiResponse(
    requestAction: () => dio.get('/user'),
    mapper: User.fromJson,
  );
}

而不是以下冗长的代码:

Future<Either<ApiFailure, User>> fetchUser() async {
  final dio = Dio(BaseOptions(baseUrl: 'https://example.com'));
  try {
    final request = await dio.get('/user');
    final data = request.data?['data'];
    if (data == null) {
      final error = request.data?['error'];
      if (error != null) {
        return left(ApiFailure.serverFailure(error['message']));
      } else {
        return left(ApiFailure.unknown());
      }
    } else {
      return right(User.fromJson(data));
    }
  } catch (error, st) {
    ApiFailure? apiFailure;
    if (error is DioError) {
      final responseFailure = error.response?.data;
      if (responseFailure is Map<String, dynamic>) {
        apiFailure = ApiFailure.serverFailure(responseFailure['message']);
      } else {
        apiFailure = ApiFailure.httpError(error.response?.statusCode);
      }
    }
    return left(apiFailure ?? ApiFailure.unknown());
  }
}

如果你也想编写这样的简洁代码,请继续阅读!

不知道什么是 Either

Either 是来自 fpdart 包 的一个类型。它用于返回要么是错误(左),要么是数据(右)。

使用方法

为了使用 response_parser,你需要进行一些准备工作。假设你的服务器返回如下的响应:

{
  "data": {
    // 请求的数据
  },
  "error": {
    // 服务器错误,需要解析并显示给用户
    "message": "Something went wrong"
  }
}

并且你的错误模型如下所示:

class ApiFailure {
  factory ApiFailure.unknown() = _UnknownApiFailure;
  factory ApiFailure.serverFailure(String errorMessage) = _ServerFailure;
  factory ApiFailure.httpError(int? statusCode) = _HttpError;
}

你需要实现 dataExtractorfailureParsererrorCatcher,如下所示:

final _exampleResponseParser = ResponseParser<Response, ApiFailure>(
  dataExtractor: (response) => response.data['data']!,
  failureParser: (response) {
    final error = response.data['error'];
    if (error is Map<String, dynamic>) {
      return ApiFailure.serverFailure(error['message']);
    } else {
      return null;
    }
  },
  errorCatcher: (error, stackTrace) {
    ApiFailure? apiFailure;
    if (error is DioError) {
      apiFailure = ApiFailure.httpError(error.response?.statusCode);
    }
    return apiFailure ?? ApiFailure.unknown();
  },
);

然后创建顶级的 parseApiResponseparseListApiResponseparseEmptyApiResponse 函数:

final parseApiResponse = _exampleResponseParser.parseApiResponse;
final parseListApiResponse = _exampleResponseParser.parseListApiResponse;
final parseEmptyApiResponse = _exampleResponseParser.parseEmptyApiResponse;

工作原理

下图展示了 parseApiResponse 方法的工作原理:

parseApiResponse diagram

实际上,parseApiResponse 方法中的所有操作都包裹在一个 try-catch 块中,因此该方法是安全的,不会抛出任何异常。

另一种使用方式

除了创建顶级函数,你还可以创建一个继承自 ResponseParserBase 的类,并重写其方法:

class DefaultResponseParser extends ResponseParserBase<Response, ApiFailure> {
  @override
  Object extractData(Response response) => response.data['data']!;

  @override
  Failure? parseFailure(Response response) {
    final error = response.data['error'];
    if (error is Map<String, dynamic>) {
      return ApiFailure.serverFailure(error['message']);
    } else {
      return null;
    }
  }

  @override
  Failure catchError(Object error, StackTrace stackTrace) {
    ApiFailure? apiFailure;
    if (error is DioError) {
      apiFailure = ApiFailure.httpError(error.response?.statusCode);
    }

    return apiFailure ?? ApiFailure.unknown();
  }
}

示例代码

以下是一个完整的示例代码,展示了如何使用 response_parser 插件:

import 'package:dio/dio.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:response_parser/response_parser.dart';

part 'example.freezed.dart';
part 'example.g.dart';

final _exampleResponseParser = ResponseParser<Response, ApiFailure>(
  dataExtractor: (response) => response.data['data']!,
  failureParser: (response) => _parseApiFailure(response.data),
  errorCatcher: (error, stackTrace) {
    ApiFailure? apiFailure;
    if (error is DioError) {
      final responseFailure = error.response?.data;
      if (responseFailure is Map<String, dynamic>) {
        apiFailure = _parseApiFailure(responseFailure);
      } else {
        apiFailure = ApiFailure.httpError(error.response?.statusCode);
      }
    }

    return apiFailure ?? ApiFailure.unknown();
  },
);

ApiFailure? _parseApiFailure(Map<String, dynamic> json) {
  final error = json['error'];
  if (error is Map<String, dynamic>) {
    return ApiFailure.serverFailure(error['message']);
  } else {
    return null;
  }
}

final parseApiResponse = _exampleResponseParser.parseApiResponse;
final parseListApiResponse = _exampleResponseParser.parseListApiResponse;
final parseEmptyApiResponse = _exampleResponseParser.parseEmptyApiResponse;

Future<void> main() async {
  final dio = Dio(BaseOptions(baseUrl: 'https://example.com'));

  final userResult = await parseApiResponse(
    requestAction: () => dio.get('/user'),
    mapper: User.fromJson,
  );
  userResult.fold(
    (failure) => print('Error: ${failure.displayError}'),
    (user) => print('User is loaded: $user'),
  );

  final usersResult = await parseListApiResponse(
    requestAction: () => dio.get('/users'),
    mapper: User.fromJson,
  );
  usersResult.fold(
    (failure) => print('Error: ${failure.displayError}'),
    (users) => print('Users are loaded: $users'),
  );

  final deleteUserResult = await parseEmptyApiResponse(
    requestAction: () => dio.delete('/user'),
  );
  deleteUserResult.match<void>(
    () => print('User is successfully deleted!'),
    (failure) => print('Error: ${failure.displayError}'),
  );
}

@JsonSerializable()
class User {
  User(this.name);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  final String name;
}

@freezed
class ApiFailure with _$ApiFailure {
  ApiFailure._();
  factory ApiFailure.unknown() = _UnknownApiFailure;
  factory ApiFailure.serverFailure(String errorMessage) = _ServerFailure;
  factory ApiFailure.httpError(int? statusCode) = _HttpError;

  String get displayError => when(
        unknown: () => 'Unknown',
        serverFailure: (errorMessage) => errorMessage,
        httpError: (statusCode) => 'Http error with status code $statusCode',
      );
}

以上就是 response_parser 插件的使用方法。希望对你有所帮助!如果有更多问题,可以查看示例代码或查阅官方文档。


更多关于Flutter响应数据解析插件response_parser的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter响应数据解析插件response_parser的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用response_parser插件来解析响应数据的示例代码。response_parser插件可以简化从HTTP响应中提取数据的过程,尤其是当响应数据为JSON格式时。

首先,确保你已经在pubspec.yaml文件中添加了response_parser依赖:

dependencies:
  flutter:
    sdk: flutter
  response_parser: ^x.y.z  # 替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,下面是一个完整的示例,展示了如何使用response_parser来解析HTTP响应数据。

示例代码

  1. 定义数据模型

假设你有一个简单的用户数据模型:

class User {
  final String id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as String,
      name: json['name'] as String,
      email: json['email'] as String,
    );
  }
}
  1. 使用response_parser解析响应

接下来,在你的Flutter应用中,你可以使用dio库(response_parser通常与dio一起使用)来发起HTTP请求,并使用response_parser来解析响应。

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:response_parser/response_parser.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  User? user;

  @override
  void initState() {
    super.initState();
    _fetchUserData();
  }

  Future<void> _fetchUserData() async {
    final Dio dio = Dio();
    final ResponseParser parser = ResponseParser<User>(
      (data) => User.fromJson(data),
    );

    try {
      final Response<dynamic> response = await dio.get(
        'https://api.example.com/users/1', // 替换为实际的API端点
        options: Options(
          responseType: ResponseType.json,
        ),
      );

      final Result<User> result = parser.parse(response.data);

      if (result.success) {
        setState(() {
          user = result.data;
        });
      } else {
        // 处理解析错误
        print('Error parsing response: ${result.message}');
      }
    } catch (e) {
      // 处理网络或其他异常
      print('Error fetching data: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('User Data'),
        ),
        body: Center(
          child: user == null
              ? CircularProgressIndicator()
              : Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text('ID: ${user!.id}'),
                    Text('Name: ${user!.name}'),
                    Text('Email: ${user!.email}'),
                  ],
                ),
        ),
      ),
    );
  }
}

解释

  • 数据模型User类定义了用户数据的结构,并包含了一个从JSON数据构造User对象的工厂方法。
  • HTTP请求:使用dio库发起HTTP GET请求。
  • 响应解析:使用ResponseParser来解析响应数据。ResponseParser的泛型参数指定了要解析的数据类型(在本例中是User),并且提供了一个函数来将JSON数据转换为User对象。
  • UI更新:如果解析成功,则更新UI以显示用户数据;如果解析失败,则打印错误信息。

这个示例展示了如何在Flutter应用中使用response_parser插件来简化HTTP响应数据的解析过程。你可以根据实际需求调整代码,例如处理更复杂的响应结构或添加更多的错误处理逻辑。

回到顶部