Flutter RESTful API请求插件brick_rest的使用

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

Flutter RESTful API 请求插件 brick_rest 的使用

在本指南中,我们将探讨如何在 Flutter 应用程序中使用 brick_rest 插件来处理 RESTful API 请求。该插件提供了一种简单的方式来与 RESTful API 进行交互,并且可以与 brick_corebrick_offline_first_with_rest_build 等其他插件结合使用。

REST 提供者

brick_rest 通过 RESTful API 连接 brick。它提供了与 RESTful API 进行交互的功能,并允许你在应用程序中轻松地管理数据。

支持的查询配置

providerArgs

providerArgs 参数允许你指定关于请求的信息,例如 HTTP 方法或顶级键。

'providerArgs': (
  'request': RestRequest(
    // 配置请求信息
  )
)

where

RestProvider 不支持任何 Query#where 参数。这些参数应根据模型按模型基础进行配置。

模型

@RestSerializable(requestTransformer:)

每个 REST API 的构建方式都不同,并且可能存在技术债务。brick 提供了灵活性,可以在任何系统中处理不一致的端点。端点也可以根据查询更改。

由于 Dart 要求注解为常量,因此无法使用动态函数。这可能会导致一些问题。相反,可以使用 const 构造函数撕裂以在运行时基于查询参数或 Dart 实例(仅限 upsertdelete)动态定义请求(方法、顶级键、URL 等)。

class UserRequestTransformer extends RestRequestTransformer {
  final get = const RestRequest(url: '/users');
  const UserRequestTransformer(Query? query, RestModel? instance) : super(query, instance);
}

@ConnectOfflineFirstWithRest(
  restConfig: RestSerializable(
    requestTransformer: UserRequestTransformer.new;
  )
)
class User extends OfflineFirstModel {}

不同的提供者调用将使用不同的转换器字段:

class UserRequestTransformer extends RestRequestTransformer {
  final get = const RestRequest(url: '/users');
  final delete = RestRequest(url: '/users/${instance.id}');

  const UserRequestTransformer(Query? query, Model? instance) : super(query, instance);
}

@ConnectOfflineFirstWithRest(
  restConfig: RestSerializable(
    requestTransformer: UserRequestTransformer.new,
  )
)
class User extends OfflineFirstModel {}

如果 RestRequestTransform 的方法字段(getupsertdelete)为 null 或其 urlnull,则提供者将跳过请求。

使用 Query#providerArgs
class UserRequestTransformer extends RestRequestTransformer {
  RestRequest? get get {
    if (query?.providerArgs.isNotEmpty && query.limit != null) {
      return RestRequest(url: "/users?limit=${query.limit}");
    }
    const RestRequest(url: '/users');
  }

  final delete = RestRequest(url: '/users/${instance.id}');

  const UserRequestTransformer(Query? query, RestModel? instance) : super(query, instance);
}

@ConnectOfflineFirstWithRest(
  restConfig: RestSerializable(
    requestTransformer: UserRequestTransformer.new,
  )
)
class User extends OfflineFirstModel {}
使用 Query#where
class UserRequestTransformer extends RestRequestTransformer {
  RestRequest? get get {
    if (query?.where != null) {
      final id = Where.firstByField('id', query.where)?.value;
      if (id != null) return RestRequest(url: "/users/$id");
    }
    const RestRequest(url: '/users');
  }

  final delete = RestRequest(url: '/users/${instance.id}');

  const UserRequestTransformer(Query? query, RestModel? instance) : super(query, instance);
}

@ConnectOfflineFirstWithRest(
  restConfig: RestSerializable(
    requestTransformer: UserRequestTransformer.new,
  )
)
class User extends OfflineFirstModel {}

为了便于说明,代码被假定为转换器和模型逻辑在同一文件中。强烈建议将请求转换器逻辑包含在其自己的文件中(例如 user.model.request.dart)。

@RestRequest(topLevelKey:)

数据通常会在 JSON 响应中的顶级键下嵌套。键由以下优先级决定:

  1. Query#providerArgs['request'] 中的 topLevelKey,其值非空。
  2. RestRequest 中定义的 topLevelKey
  3. 发现的第一个键。由于映射本质上是一个无序列表,因此依赖于此回退是不推荐的。
class UserRequestTransformer extends RestRequestTransformer {
  final get = const RestRequest(url: '/users', topLevelKey: 'users');
  final upsert = const RestRequest(url: '/users', topLevelKey: 'user');
  const UserRequestTransformer(Query? query, RestModel? instance) : super(query, instance);
}

@ConnectOfflineFirstWithRest(
  requestTransformer: UserRequestTransformer.new
)
class User extends OfflineFirstModel {}

如果来自 REST 的响应不是映射,则返回整个响应。

@RestSerializable(fieldRename:)

brick 通过假设标准命名约定减少了将 REST 键映射到模型字段名称的需求。例如:

RestSerializable(fieldRename: FieldRename.snake_case)
// 在从 REST 获取时
 "last_name" => final String lastName
// 在向 REST 更新时
final String lastName => "last_name"

字段

@Rest(enumAsString:)

brick 默认假设 REST API 会以与 Flutter 应用程序中的枚举匹配的索引作为整数形式传递。但是,如果你的 API 传递字符串而不是整数,可以通过简单的注解而不编写自定义生成器来处理这种情况。

给定 API:

{ "user": { "hats": ["bowler", "birthday"] } }

只需将 hats 转换为 Dart 枚举:

enum Hat { baseball, bowler, birthday }

...

@Rest(enumAsString: true)
final List<Hat> hats;

@Rest(name:)

REST 键可以根据字段重命名。这将覆盖由 RestSerializable#fieldRename 设置的默认值。

// "full_name" 在从和到 REST 请求中使用,而不是 "last_name"
@Rest(name: "full_name")
final String lastName;

@Rest(ignoreFrom:)@Rest(ignoreTo:)

当为 true 时,字段将被适配器中的(反)序列化函数忽略。

GZIP 请求压缩

所有对 API 端点的请求都可以使用 Dart 的标准 GZip 库进行压缩。所有请求将覆盖 Content-Encoding 头为 {'Content-Encoding': 'gzip'}

import 'package:brick_rest/gzip_http_client.dart';

final restProvider = RestProvider(client: GZipHttpClient(level: 9));

注意:你的 API 必须能够接受并解码 GZipped 请求。

不支持的字段类型

以下字段类型未序列化为 REST。但是,不支持的类型仍然可以在模型中作为非最终字段访问。

  • 嵌套 List<> 例如 <List<List<int>>>
  • 多对多关联

完整示例代码

以下是完整的示例代码,展示了如何在 Flutter 应用程序中使用 brick_rest 插件。

import 'package:brick_core/core.dart';
import 'package:brick_rest/brick_rest.dart';

/// 这个类和代码总是由生成器生成。
/// 它在这里作为示例包含。
/// Rest 适配器由利用 `brick_rest_generators` 包的域生成,
/// 例如 `brick_offline_first_with_rest_build`
class UserAdapter extends RestAdapter<User> {
  [@override](/user/override)
  Future<User> fromRest(data, {required provider, repository}) async {
    return User(
      name: data['name'],
    );
  }

  [@override](/user/override)
  Future<Map<String, dynamic>> toRest(instance, {required provider, repository}) async {
    return {
      'name': instance.name,
    };
  }
}

/// 这个值总是由生成器生成。
/// 它在这里作为示例包含。
/// 从你的应用中导入 `lib/brick/brick.g.dart`
final dictionary = RestModelDictionary({
  User: UserAdapter(),
});

/// 模型是唯一的,具体取决于实现(例如 Flutter 应用)
class User extends RestModel {
  final String name;

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

class MyRepository extends SingleProviderRepository<RestModel> {
  MyRepository(String baseApiUrl)
      : super(
          RestProvider(
            baseApiUrl,
            modelDictionary: dictionary,
          ),
        );
}

void main() async {
  final repository = MyRepository('http://localhost:8080');

  final users = await repository.get<User>();
  print(users);
}

更多关于Flutter RESTful API请求插件brick_rest的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter RESTful API请求插件brick_rest的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter中使用brick_rest插件来进行RESTful API请求的示例代码。brick_rest是一个用于简化HTTP请求的Flutter插件。

首先,你需要在你的pubspec.yaml文件中添加brick_rest依赖:

dependencies:
  flutter:
    sdk: flutter
  brick_rest: ^最新版本号  # 请替换为最新版本号

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

接下来,你可以在Flutter项目中使用brick_rest来进行API请求。以下是一个完整的示例,展示了如何进行GET和POST请求:

import 'package:flutter/material.dart';
import 'package:brick_rest/brick_rest.dart';
import 'dart:convert';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter brick_rest Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String? responseData;
  String? errorMessage;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('brick_rest Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextButton(
              onPressed: () => fetchData(),
              child: Text('Fetch Data (GET)'),
            ),
            SizedBox(height: 16),
            TextButton(
              onPressed: () => postData(),
              child: Text('Post Data (POST)'),
            ),
            SizedBox(height: 16),
            if (responseData != null)
              Text(
                'Response Data:\n$responseData',
                style: TextStyle(fontSize: 16),
              ),
            if (errorMessage != null)
              Text(
                'Error:\n$errorMessage',
                style: TextStyle(fontSize: 16, color: Colors.red),
              ),
          ],
        ),
      ),
    );
  }

  void fetchData() async {
    try {
      var response = await BrickRest.get(
        url: 'https://jsonplaceholder.typicode.com/posts/1',
      );

      if (response.statusCode == 200) {
        setState(() {
          responseData = jsonEncode(jsonDecode(response.body));
          errorMessage = null;
        });
      } else {
        setState(() {
          responseData = null;
          errorMessage = 'Failed to fetch data. Status code: ${response.statusCode}';
        });
      }
    } catch (e) {
      setState(() {
        responseData = null;
        errorMessage = e.toString();
      });
    }
  }

  void postData() async {
    try {
      var response = await BrickRest.post(
        url: 'https://jsonplaceholder.typicode.com/posts',
        body: jsonEncode({
          'title': 'foo',
          'body': 'bar',
          'userId': 1,
        }),
        headers: {
          'Content-Type': 'application/json',
        },
      );

      if (response.statusCode == 201) {
        setState(() {
          responseData = jsonEncode(jsonDecode(response.body));
          errorMessage = null;
        });
      } else {
        setState(() {
          responseData = null;
          errorMessage = 'Failed to post data. Status code: ${response.statusCode}';
        });
      }
    } catch (e) {
      setState(() {
        responseData = null;
        errorMessage = e.toString();
      });
    }
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,包含两个按钮:一个用于执行GET请求,另一个用于执行POST请求。GET请求从https://jsonplaceholder.typicode.com/posts/1获取一个帖子,而POST请求向https://jsonplaceholder.typicode.com/posts发送一个新的帖子。

请注意,BrickRest.getBrickRest.post方法返回的是一个Response对象,你可以检查其statusCode属性来确定请求是否成功,并读取body属性来获取响应数据。

请确保替换示例中的URL和请求体数据以匹配你的实际API端点和数据需求。

回到顶部