在 Flutter 中使用 http 包【Flutter 专题 2】

简介: 在 Flutter 中使用 http相信大家在使用应用的时候都发现了,大多数应用程序必须通过互联网执行网络请求。因此,优雅地处理网络调用以避免 API 调用中出现不必要的错误非常重要。

在 Flutter 中使用 http

相信大家在使用应用的时候都发现了,


大多数应用程序必须通过互联网执行网络请求。因此,优雅地处理网络调用以避免 API 调用中出现不必要的错误非常重要。


在本文中,我们将看看如何在 Flutter 中使用http包处理 REST API 请求。


这也是非常重要的一个环节,如果你想与后端交互,就不得不用到它,当然,还有另外一个 dio 包,我也会在后面给大家做介绍。

入门

使用以下命令创建一个新的 Flutter 项目:


flutter create flutter_http_networking

复制代码


您可以使用自己喜欢的 IDE 打开项目,但在本示例中,我将使用 VS Code:


code flutter_http_networking

复制代码


http 包添加到您的pubspec.yaml文件中:


在这里,我将 http 包的文档放到这儿,以供大家查阅https://pub.flutter-io.cn/packages/http 在 pub.dev 有 3600+喜欢,所以大家可以看到他的实力。


dependencies:  http: ^0.13.3

复制代码


用以下基本结构替换main.dart文件的内容:


import 'package:flutter/material.dart';
import 'screens/home_page.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Networking',
      theme: ThemeData(
        primarySwatch: Colors.teal,
      ),
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

复制代码


我们在执行网络操作的 API 之后,我们将创建 HomePage。

请求 API 数据

对于 API 请求的演示,我们将使用来自JSONPlaceholder的示例数据。这是一个用于测试和原型制作的免费假 API。


我们将首先使用 GET 请求获取单个帖子数据。您必须使用的端口是:/posts


GET https://jsonplaceholder.typicode.com/posts/<id>

复制代码


在这里,您必须将 替换为<id>代表您要检索的帖子 ID 的整数值。


如果您的请求成功,您将收到的示例 JSON 响应将如下所示:


{
  "userId" : 1 , 
  "id" : 1 , 
  "title" : "sunt aut facere repellat Provident occaecati excepturi optio reprehenderit" , 
  "body" : "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit moestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" 
}

复制代码

指定模型类

模型类可帮助您打包 API 调用返回的数据或使用网络请求整齐地发送数据。


我们将定义一个模型类来处理单个帖子数据。您可以使用JSON-to-Dart 类转换工具来轻松生成模型类。将其复制并粘贴到名为 post.dart 的文件中:,这个网站简直不要太爽哦,,


https://app.quicktype.io/


class Post {
  Post({
    this.id,
    this.userId,
    this.title,
    this.body,
  });
  int? id;
  int? userId;
  String? title;
  String? body;
  factory Post.fromJson(Map<String, dynamic> json) => Post(
        userId: json["userId"],
        id: json["id"],
        title: json["title"],
        body: json["body"],
      );
  Map<String, dynamic> toJson() => {
        "userId": userId,
        "id": id,
        "title": title,
        "body": body,
      };
}

复制代码


或者,您也可以使用 JSON 序列化并自动生成fromJsontoJson方法,这有助于防止在手动定义时可能发生的任何未注意到的错误。


如果您使用 JSON 序列化,您将需要以下包:


  • json_serializable
  • json_annotation
  • build_runner


将它们添加到您的pubspec.yaml文件中:


dependencies:
  json_annotation: ^4.0.1
dev_dependencies:
  json_serializable: ^4.1.3
  build_runner: ^2.0.4



要使用 JSON 序列化,您必须按如下方式修改post类:


import 'package:json_annotation/json_annotation.dart';
part 'post.g.dart';
@JsonSerializable()
class Post {
  Post({
    this.id,
    this.userId,
    this.title,
    this.body,
  });
  int? id;
  int? userId;
  String? title;
  String? body;
  factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
  Map<String, dynamic> toJson() => _$PostToJson(this);
}



您可以使用以下命令触发代码生成工具:


flutter pub run build_runner build



如果你想让代码生成工具在后台运行——它会自动应用你对模型类所做的任何进一步修改——使用命令:


flutter pub run build_runner serve --delete-conflicting-outputs



如果发现任何冲突,--delete-conflicting-outputs标志有助于重新生成类的一部分。

执行 API 请求

现在您可以开始在 REST API 上执行各种网络请求。为了保持代码整洁,您可以在单独的类中定义与网络请求相关的方法。


创建一个名为post_client.dart 的新文件,并在其中定义类:PostClient


class PostClient {
  // TODO: Define the methods for network requests
}



在变量中定义服务器的基本 URL 以及所需的端口:


class PostClient {
  static final baseURL = "https://jsonplaceholder.typicode.com";
  static final postsEndpoint = baseURL + "/posts";
}



我们将在执行请求时使用这些变量。

获取数据

您可以使用 GET 请求从 API 检索信息。要获取单个帖子数据,您可以定义这样的方法:


Future<Post> fetchPost(int postId) async {
  final url = Uri.parse(postsEndpoint + "/$postId");
  final response = await http.get(url);
}



此方法尝试根据传递给它的 ID 检索帖子数据。该方法使用 URL 从服务器获取存储在变量中的数据。http.get()``response


通过检查 HTTP 状态代码来验证请求是否成功,如果成功应该是200。您现在可以使用 Post.fromJson()解码原始 JSON 数据,并使用模型类以良好的结构化方式存储它。``


Future<Post> fetchPost(int postId) async {
  final url = Uri.parse(postsEndpoint + "/$postId");
  final response = await http.get(url);
  if (response.statusCode == 200) {
    return Post.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to load post: $postId');
  }
}


发送数据

您可以使用 POST 请求向 API 发送数据。我们将通过使用以下方法发送数据来创建一个新帖子:


uture<Post> createPost(String title, String body) async {
  final url = Uri.parse(postsEndpoint);
  final response = await http.post(
    url,
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode({
      'title': title,
      'body': body,
    }),
  );
}



发送数据时,您必须指定标头类型headers以及body要发送到指定端口的标头类型。此外,JSON 数据应使用该jsonEncode方法以编码格式发送。


您可以使用 HTTP 状态代码检查您的 POST 请求是否成功。如果返回状态码为201,则请求成功,我们可以返回 post 数据。


Future<Post> createPost(String title, String body) async {
  // ...
  if (response.statusCode == 201) {
    return Post.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to create post');
  }
}


更新数据

您可以使用 PUT 请求更新 API 服务器上存在的任何发布信息。像这样定义方法:


Future<Post> updatePost(int postId, String title, String body) async {
  final url = Uri.parse(postsEndpoint + "/$postId");
  final response = await http.put(
    url,
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: jsonEncode({
      'title': title,
      'body': body,
    }),
  );
}



在这里,我们使用了帖子 ID 来指定要更新和发送请求的帖子。如果响应状态码为200,则请求成功,您可以返回服务器发送的更新后的帖子。


Future<Post> updatePost(int postId, String title, String body) async {
  // ...
  if (response.statusCode == 200) {
    return Post.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to update post');
  }
}


删除数据

您可以使用 DELETE 请求从 API 服务器中删除帖子。该方法可以定义如下:


Future<Post> deletePost(int postId) async {
  final url = Uri.parse(postsEndpoint + "/$postId");
  final response = await http.delete(
    url,
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
    },
  );
  if (response.statusCode == 200) {
    return Post.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to delete post: $postId');
  }
}


我们使用帖子 ID 来指定要删除的帖子并将请求发送到相应的端口。您可以通过检查 HTTP 状态代码是否为 200来验证请求是否成功。


您应该注意,在删除的情况下,响应返回空数据。


Future<Post> deletePost(int postId) async {
  //...
  if (response.statusCode == 200) {
    return Post.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to delete post: $postId');
  }
}


构建用户界面

用户界面将在HomePage小部件中定义。这将是一个StatefulWidget因为我们需要在每个网络请求之后更新它的状态。


import 'package:flutter/material.dart';
import 'package:flutter_http_networking/utils/post_client.dart';
import 'package:http/http.dart' as http;
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold();
  }
}



首先,我们将定义将存储的两个变量title,和body并通过 API 调用返回的帖子,然后我们会初始化PostClient类:


import 'package:flutter/material.dart';
import 'package:flutter_http_networking/utils/post_client.dart';
import 'package:http/http.dart' as http;
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
  final PostClient _postClient = PostClient();
  String? _postTitle;
  String? _postBody;
  @override
  Widget build(BuildContext context) {
    return Scaffold();
  }
}



我们现在将创建将触发网络请求方法并使用服务器返回的信息更新变量的按钮。下面是触发该方法的代码片段:fetchPost()


ElevatedButton(
  onPressed: () async {
    final post = await _postClient.fetchPost(1);
    setState(() {
      _postTitle = post.title;
      _postBody = post.body;
    });
  },
  child: Text('GET'),
)



您可以类似地触发其余的网络请求方法。


最终的应用程序 UI 如下所示:


image.png

测试网络请求

您可以使用Mockito测试 Dart 中的 API 请求,该包有助于模拟网络请求并测试您的应用程序是否有效处理各种类型的请求,包括空响应和错误响应。


要使用 Mockito 进行测试,请将其添加到您的文件中,并确保您有如下依赖


dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.0.4
  mockito: ^5.0.10

复制代码


现在,您必须对要测试的网络请求方法进行一些小的修改。我们将对方法进行修改。为fetchPost()


方法提供一个get(),并使用http.Client客户端来执行请求。修改后的方法将如下所示:


Future<Post> fetchPost(http.Client client, int postId) async {
  final url = Uri.parse(postsEndpoint + "/$postId");
  final response = await client.get(url);
  if (response.statusCode == 200) {
    return Post.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('Failed to load post: $postId');
  }
}



创建一个fetch_post_test.dart文件在test夹内调用的测试文件,并添加方法.@GenerateMocks([http.Client])


import 'package:flutter_http_networking/models/post.dart';
import 'package:flutter_http_networking/utils/post_client.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'fetch_post_test.mocks.dart';
@GenerateMocks([http.Client])
void main() {}



这将有助于MockClient使用该build_runner工具生成类。使用以下命令触发代码生成:


flutter pub run build_runner build



我们将只定义两个测试:一个针对成功的 API 请求,另一个针对有错误的不成功请求。您可以使用 Mockito 提供的函数测试这些条件。when()


@GenerateMocks([http.Client])
void main() {
  PostClient _postClient = PostClient();
  final postEndpoint =
      Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
  group('fetchPost', () {
    test('successful request', () async {
      final client = MockClient();
      when(
        client.get(postEndpoint),
      ).thenAnswer((_) async => http.Response(
          '{"userId": 1, "id": 2, "title": "mock post", "body": "post body"}',
          200));
      expect(await _postClient.fetchPost(client, 1), isA<Post>());
    });
    test('unsuccessful request', () {
      final client = MockClient();
      when(
        client.get(postEndpoint),
      ).thenAnswer((_) async => http.Response('Not Found', 404));
      expect(_postClient.fetchPost(client, 1), throwsException);
    });
  });
}
   });  });}



您可以使用以下命令运行测试:


flutter test test/fetch_post_test.dart



或者,您也可以直接在 IDE 中运行测试。当我使用 VS Code 运行测试时,我收到了这个结果:


image.png

结论

http包有助于在 Flutter 中执行所有类型的网络请求。它还提供对 Mockito 的支持,从而简化了 API 调用的测试。


如果你想对你的请求进行更高级的控制,那么你可以使用 Dio 包,它有助于避免一些样板代码,并支持全局配置、拦截器、转换器和其他功能。

项目地址:https://github.com/ITmxs/flutter_http_networking

相关文章
|
30天前
|
中间件 Go 开发者
Go net http包
Go net http包
39 0
|
30天前
|
资源调度
#发布npm包遇到错误,因为用了淘宝镜像地址的原因的解决方法-403 403 Forbidden - PUT https://registry.npmmirror.com/-/user/org.cou
#发布npm包遇到错误,因为用了淘宝镜像地址的原因的解决方法-403 403 Forbidden - PUT https://registry.npmmirror.com/-/user/org.cou
203 0
|
8月前
|
Go
Go 使用标准库 net/http 包构建服务器
Go 使用标准库 net/http 包构建服务器
29 0
|
21天前
|
Android开发 数据安全/隐私保护 iOS开发
ios和安卓测试包发布网站http://fir.im的注册与常用功能
ios和安卓测试包发布网站http://fir.im的注册与常用功能
19 0
ios和安卓测试包发布网站http://fir.im的注册与常用功能
|
30天前
|
供应链 安全 开发者
供应链投毒预警:恶意Py包伪装HTTP组件开展CStealer窃密后门攻击
近日(2024年4月25号),悬镜供应链安全情报中心在Pypi官方仓库(https://pypi.org/)中捕获1起CStealer窃密后门投毒事件,投毒者连续发布6个不同版本的恶意Py包multiplerequests,目标针对windows平台python开发者,该恶意包在安装时会远程加载CStealer后门到受害者系统上执行,该后门会窃取受害者系统敏感信息、主流浏览器隐私数据、数字货币钱包应用数据以及系统屏幕截屏等。此外,后门还会尝试驻留Windows系统启动目录实现开机自启动。
29 0
供应链投毒预警:恶意Py包伪装HTTP组件开展CStealer窃密后门攻击
|
30天前
|
JSON 编解码 Go
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第25天】Go语言`net/http`包提供HTTP客户端和服务器功能,简化高性能网络应用开发。本文探讨如何发起HTTP请求,常见问题及解决策略。示例展示GET和POST请求的实现。注意响应体关闭、错误处理、内容类型设置、超时管理和并发控制。最佳实践包括重用`http.Client`,使用`context.Context`,处理JSON以及记录错误日志。通过实践这些技巧,提升HTTP编程技能。
30 1
|
30天前
|
Go 开发者
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第24天】Go语言的`net/http`包在HTTP客户端编程中扮演重要角色,但使用时需注意几个常见问题:1) 检查HTTP状态码以确保请求成功;2) 记得关闭响应体以防止资源泄漏;3) 设置超时限制,避免长时间等待;4) 根据需求处理重定向。理解这些细节能提升HTTP客户端编程的效率和质量。
24 1
|
30天前
|
存储 缓存 开发框架
Flutter的网络请求:使用Dart进行HTTP请求的技术详解
【4月更文挑战第26天】了解Flutter网络请求,本文详述使用Dart进行HTTP请求
|
30天前
|
安全 网络安全 开发工具
对象存储oss使用问题之flutter使用http库进行post请求文件上传返回400如何解决
《对象存储OSS操作报错合集》精选了用户在使用阿里云对象存储服务(OSS)过程中出现的各种常见及疑难报错情况,包括但不限于权限问题、上传下载异常、Bucket配置错误、网络连接问题、跨域资源共享(CORS)设定错误、数据一致性问题以及API调用失败等场景。为用户降低故障排查时间,确保OSS服务的稳定运行与高效利用。
|
30天前
|
网络协议
Wireshark中的http协议包分析
Wireshark可以跟踪网络协议的通讯过程,本节通过http协议,在了解Wireshark使用的基础上,重温http协议的通讯过程。 TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。 HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议,是万维网的数据通信的基础。 下图是访问百度页面的头部文件的Wireshark数据包截取图,以下几点说明如下:
Wireshark中的http协议包分析