Flutter微框架Nylo(二):网络

1. 介绍

Nylo 使应用程序中的网络请求变得简单。您可以通过基网络类执行 GET、PUT、POST 和 DELETE 请求。内部网络库使用Dio,一个强大的HTTP客户端。

默认的 API 服务目录位于此处 app/networking/*

Nylo 的模版将包括默认的 API 服务 app/networking/api_service.dart

dart 复制代码
class ApiService extends BaseApiService {
  ApiService({BuildContext? buildContext}) : super(buildContext);

  @override
  String get baseUrl => "https://jsonplaceholder.typicode.com";

  @override
  final interceptors = {
    LoggingInterceptor: LoggingInterceptor()
  };

  Future<User?> fetchUsers() async {
    return await network<User>(
        request: (request) => request.get("/users"),
    );
}

使用 BaseApiService 类可覆盖的变量。

  • baseUrl - 这是 API 的基本 URL,例如"jsonplaceholder.typicode.com"。
  • interceptors - 在这里,您可以添加 Dio 拦截器。在此处了解有关拦截器的更多信息。
  • useInterceptors - 您可以将其设置为 true 或 false。它将让 API 服务知道是否使用您的拦截器。

2. HTTP 请求

在 API 服务中,使用 network 方法生成 API 请求。

dart 复制代码
class ApiService extends BaseApiService {
  ApiService({BuildContext? buildContext}) : super(buildContext);

  @override
  String get baseUrl => "https://jsonplaceholder.typicode.com";

  Future<dynamic> fetchUsers() async {
    return await network(
        request: (request) {
          // return request.get("/users"); // GET请求
          // return request.put("/users", data: {"user": "data"}); // PUT请求
          // return request.post("/users", data: {"user": "data"}); // POST请求
          // return request.delete("/users/1"); // DELETE请求

          return request.get("/users");
        },
    );
  }
}

参数 request 是一个 Dio 实例,因此您可以从该对象调用所有方法。

3. 基础设置

BaseOptions 变量是高度可配置的,允许修改 API 请求设置。

在 API 服务中,可以重写构造函数。

dart 复制代码
class ApiService extends BaseApiService {
  ApiService({BuildContext? buildContext}) : super(buildContext) {
    baseOptions = BaseOptions(
      receiveTimeout: 10000,
      connectTimeout: 5000
    );
  }
...

单击此处查看所有设置。

4. 添加请求头

可以通过 baseOptions 变量,在每个请求或拦截器上向请求添加头信息。

下面是向请求添加标头的最简单方法。

dart 复制代码
Future fetchWithHeaders() async => await network(
    request: (request) => request.get("/test"),
    headers: {
      "Authorization": "Bearer aToken123",
      "Device": "iPhone"
    }
);

您还可以添加 Bearer Token,如以下示例所示

dart 复制代码
Future fetchUserWithBearer() async => await network(
    request: (request) => request.get("/test"),
    bearerToken: "helloworlddd!",
);

或者

dart 复制代码
Future fetchUsers() async {
    return await network(
        request: (request) {
          request.options.headers = {
            "Authorization": "Bearer $token"
          };

          return request.get("/users");
        },
    );
}

5. 拦截器

如果您还不熟悉拦截器,请不要担心。它们是管理HTTP请求发送方式的新概念。

简单来说。"拦截器"将拦截请求,允许您在发送请求之前修改请求,在请求完成后处理响应以及如果出现错误会发生什么。

Nylo 允许您向 API 服务添加新的拦截器,如以下示例所示。

dart 复制代码
class ApiService extends BaseApiService {
  ApiService({BuildContext? buildContext}) : super(buildContext);
  ...

  @override
  final interceptors = {
    LoggingInterceptor: LoggingInterceptor(),

    // 添加更多拦截器
    // BearerAuthInterceptor: BearerAuthInterceptor(),
  };

...

自定义拦截器的示例。

dart 复制代码
import 'package:nylo_framework/nylo_framework.dart';

class CustomInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // options - 发送请求之前修改请求

    return super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // response - 处理响应内容

    handler.next(response);
  }

  @override
  void onError(DioError error, ErrorInterceptorHandler handler) {
    // error - 处理请求错误
    handler.next(err);
  }
}

Nylo 上的模版将包含一个 app/networking/dio/intecetors/* 目录。

在此文件夹中,您将找到一个 LoggingInterceptor 类。

app/networking/dio/intecetors/logging_interceptor.dart 文件:

dart 复制代码
import 'dart:developer';
import 'package:nylo_framework/nylo_framework.dart';

class LoggingInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    
    print('REQUEST[${options.method}] => PATH: ${options.path}');
    
    return super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    
      print(
          'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions
              .path}');

      print('DATA: ${response.requestOptions.path}');
      
      log(response.data.toString());
    
    handler.next(response);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    
      print('ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions
          .path}');

    handler.next(err);
  }
}

6. network方法

network 为我们提供了一种从应用程序发出 HTTP 请求的方法。在 Nylo 中使用 API 服务时,可以使用network方法。

dart 复制代码
class ApiService extends BaseApiService {
  ...

  Future<dynamic> fetchUsers() async {
    return await network(
        request: (request) {
          // return request.get("/users"); // GET请求
          // return request.put("/users", data: {"user": "data"}); // PUT请求
          // return request.post("/users", data: {"user": "data"}); // POST请求
          // return request.delete("/users/1"); // DELETE请求

          return request.get("/users");
        },
    );
  }

返回类型

有两种方法可以处理来自 HTTP 请求的响应。让我们来看看两者的实际效果,这样做没有对错之分。

  • 使用Model Decoders

它们使返回对象变得容易,如以下示例所示。

dart 复制代码
class ApiService extends BaseApiService {
  ...

  Future<User?> fetchUser() async {
    return await network<User>(
        request: (request) => request.get("/users/1"),
    );
  }

config/decoders.dart文件

dart 复制代码
final modelDecoders = {
  User: (data) => User.fromJson(data), // 添加模型并处理对象的返回
  //该 `data` 参数将包含 HTTP 响应正文。

  // ...
};

此处了解有关解码器的更多信息

  • 使用handleSuccess

handleSuccess: (Response response) {} 参数可用于处理 HTTP 返回值。

仅当 HTTP 响应的状态代码等于 200 时,才调用此方法。

dart 复制代码
class ApiService extends BaseApiService {
  ...
  // 返回一个对象
  Future<User?> findUser() async {
    return await network(
        request: (request) => request.get("/users/1"),
        handleSuccess: (Response response) { // response - Dio响应对象
          dynamic data = response.data;
          return User.fromJson(data);
        }
    );
  }
  // 返回一个字符串
  Future<String?> findMessage() async {
    return await network(
        request: (request) => request.get("/message/1"),
        handleSuccess: (Response response) { // response - Dio响应对象
          dynamic data = response.data;
          if (data['name'] == 'Anthony') {
            return "It's Anthony";
          }
          return "Hello world"; 
        }
    );
  }
  // 返回一个布尔
  Future<bool?> updateUser() async {
    return await network(
        request: (request) => request.put("/user/1", data: {"name": "Anthony"}),
        handleSuccess: (Response response) { // response - Dio响应对象
          dynamic data = response.data;
          if (data['status'] == 'OK') {
            return true;
          }
          return false;
        }
    );
  }

如果 HTTP 响应返回不等于 200 的状态代码,则将调用方法 handleFailure

可以为network提供参数handleFailure: (DioError dioError) {}, 然后在函数中处理响应。

dart 复制代码
class ApiService extends BaseApiService {
  ...
  // 返回一个对象
  Future<User?> findUser() async {
    return await network(
        request: (request) => request.get("/users/1"),
        handleFailure: (DioError dioError) { // response - Dio错误对象
          dynamic data = response.data;
          // 处理返回内容

          return null;
        }
    );
  }
}

7. 使用 API 服务

当您需要从小部件调用 API 时,Nylo 中有两种不同的方法。

1). 可以创建 API 服务的新实例,然后调用要使用的方法,如以下示例所示。

dart 复制代码
class _MyHomePageState extends NyState<MyHomePage> {

  ApiService _apiService = ApiService();

  @override
  init() async {
    List<User>? users = await _apiService.fetchUsers();
    print(users); // List<User>? 实例
...

2). 使用 api 方法,此方法更短,并且通过使用 config/decoders.dart 中的 apiDecoders 变量来工作。

dart 复制代码
class _MyHomePageState extends NyState<MyHomePage> {

  @override
  init() async {
    User? user = await api<ApiService>((request) => request.fetchUser());
    print(user); // User? instance
...

使用 api 方法还允许在请求不成功时处理对用户的 UI 反馈。为此,请将 context 参数添加到 api,如以下示例所示。

dart 复制代码
// 组件
...
  User _user = User();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MaterialButton(
        onPressed: () {
          _sendFriendRequest(_user); 
        }, 
        child: Text("Send Friend Request"),
      );
    );
  }

  _sendFriendRequest(User user) async {
    bool? successful = await api<ApiService>(
      (request) => request.sendFriendRequest(user), context: context
    );
  }
...

// API服务
class ApiService extends BaseApiService {
  ...

  // Add this
  displayError(DioError dioError, BuildContext context) {
    showToastNotification(context, title: 'Oops!', description: dioError.message);
  }
}

displayError - 如果请求发生错误(例如 500 状态代码),您可以通过 Toast 通知立即向用户提供反馈。

8. 创建接口服务

要创建更多api_services,例如 user_api_service,请使用以下命令。

dart run nylo_framework:main make:api_service user

还可以使用该 --model="User" 选项为模型创建 API 服务。

dart run nylo_framework:main make:api_service user --model="User"

这将使用以下方法创建 API 服务。

dart 复制代码
class UserApiService extends BaseApiService {
  ...

  /// Return a list of users
  Future<List<User>?> fetchAll({dynamic query}) async {
    return await network<List<User>>(
        request: (request) => request.get("/endpoint-path", queryParameters: query),
    );
  }

  /// Find a User
  Future<User?> find({required int id}) async {
    return await network<User>(
      request: (request) => request.get("/endpoint-path/$id"),
    );
  }

  /// Create a User
  Future<User?> create({required dynamic data}) async {
    return await network<User>(
      request: (request) => request.post("/endpoint-path", data: data),
    );
  }

  /// Update a User
  Future<User?> update({dynamic query}) async {
    return await network<User>(
      request: (request) => request.put("/endpoint-path", queryParameters: query),
    );
  }

  /// Delete a User
  Future<bool?> delete({required int id}) async {
    return await network<bool>(
      request: (request) => request.delete("/endpoint-path/$id"),
    );
  }
}

9. 将 JSON 转换为 models

您可以使用解码器自动解码 JSON,从 API 请求解码到模型。

下面是一个使用 Nylo 解码器实现的 API 请求。

dart 复制代码
class ApiService extends BaseApiService {
  ApiService({BuildContext? buildContext}) : super(buildContext);

  Future<User?> fetchUsers() async {
    return await network<User>(
        request: (request) => request.get("/users"),
    );
  }
}

fetchUsers 方法将使用泛型处理到模型表示形式的 JSON 转换。

您首先需要将模型添加到文件中 config/decoders.dart ,如下所示。

dart 复制代码
/// 'config/decoders.dart'文件

final modelDecoders = {
  List<User>: (data) => List.from(data).map((json) => User.fromJson(json)).toList(),

  User: (data) => User.fromJson(data),

  // ...
};

在这里,您可以提供模型的不同表示形式,例如像上面那样的对象或列表。

解码器中的数据参数将包含来自 API 请求的响应正文。

要开始使用解码器,请查看文档的后续章节。

相关推荐
小远yyds17 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
无极程序员1 小时前
PHP常量
android·ide·android studio
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
萌面小侠Plus3 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
慢慢成长的码农3 小时前
Android Profiler 内存分析
android
大风起兮云飞扬丶3 小时前
Android——多线程、线程通信、handler机制
android