🌙 1.使用 Http 包
# 安装依赖
flutter pub add http
1
2
2
https://pub.dev/documentation/http/latest/ (opens new window)
🌙 1.1简单使用:
// 导入 http 包
import 'package:http/http.dart' as http;
// 定义一个 Post 类
class Post {
final int userId;
final int id;
final String title;
final String body;
Post(
{required this.userId,
required this.id,
required this.title,
required this.body});
// 将 JSON 数据转换为 Post 实例
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
// 定义一个 fetchPost() 方法
// 将HTTP响应转换为 Post 类实例
Future<Post> fetchPost([int id = 1]) async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/$id'));
if (response.statusCode == 200) {
return Post.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load post');
}
}
// 定义一个 MyApp 类
class _MyAppState extends State<HttpDemo> {
late Future<Post> post;
int id = 1;
void initState() {
super.initState();
post = fetchPost(id); // called inside initState()
}
Widget build(BuildContext context) {
return Center(
child: FutureBuilder<Post>(
future: post, // 调用 fetchPost() 方法
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
snapshot.data!.title,
style: const TextStyle(
fontSize: 18,
color: Colors.blue,
decoration: TextDecoration.underline),
),
ElevatedButton(
onPressed: () {
// SystemChannels.textInput
// .invokeMethod('TextInput.show');
setState(() {
post = fetchPost(id++);
});
},
child: const Icon(
Icons.navigate_next,
size: 30,
))
]);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const CircularProgressIndicator();
}));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
🌙 1.2支持传参:
var client = http.Client();
try {
var response = await client.post(
Uri.https('example.com', 'whatsit/create'),
body: {'name': 'doodle', 'color': 'blue'});
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
var uri = Uri.parse(decodedResponse['uri'] as String);
print(await client.get(uri));
} finally {
client.close();
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
🌙 1.3支持重试:
import 'package:http/http.dart' as http;
import 'package:http/retry.dart';
Future<void> main() async {
final client = RetryClient(http.Client());
try {
print(await client.read(Uri.http('example.org', '')));
} finally {
client.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
默认情况下,会重试任何响应状态码为503 Temporary Failure
的请求,最多重试三次
。它在第一次重试之前等待500ms,并且每次延迟增加1.5倍。所有这些都可以使用RetryClient()
构造函数进行定制。
🌙 1.4Restful
风格的 HTTP api:
Functions
delete(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) → Future<Response>
Sends an HTTP DELETE request with the given headers to the given URL.
get(Uri url, {Map<String, String>? headers}) → Future<Response>
Sends an HTTP GET request with the given headers to the given URL.
head(Uri url, {Map<String, String>? headers}) → Future<Response>
Sends an HTTP HEAD request with the given headers to the given URL.
patch(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) → Future<Response>
Sends an HTTP PATCH request with the given headers and body to the given URL.
post(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) → Future<Response>
Sends an HTTP POST request with the given headers and body to the given URL.
put(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) → Future<Response>
Sends an HTTP PUT request with the given headers and body to the given URL.
read(Uri url, {Map<String, String>? headers}) → Future<String>
Sends an HTTP GET request with the given headers to the given URL and returns a Future that completes to the body of the response as a String.
readBytes(Uri url, {Map<String, String>? headers}) → Future<Uint8List>
Sends an HTTP GET request with the given headers to the given URL and returns a Future that completes to the body of the response as a list of bytes.
runWithClient<R>(R body(), Client clientFactory(), {ZoneSpecification? zoneSpecification}) → R
Runs body in its own Zone with the Client returned by clientFactory set as the default Client.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
🌙 2.使用 dio 包
dio 是一个强大的 HTTP 网络请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器、转换器等。
# 安装依赖
flutter pub add dio
1
2
2
https://pub.dev/documentation/dio/latest/ (opens new window)
🌙 2.1简单使用:
import 'package:dio/dio.dart';
final dio = Dio();
Future<Post> fetchPost([int id = 1]) async {
final response = await dio.get('https://jsonplaceholder.typicode.com/posts/$id');
if (response.statusCode == 200) {
return Post.fromJson(response.data);
} else {
throw Exception('Failed to load post');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
🌙 2.2创建实例:
import 'package:dio/dio.dart';
import 'package:dio/dio.dart';
// 创建一个 Dio 实例
Dio dio = Dio(
// 配置 Dio 实例的选项
BaseOptions(
// 设置请求的基本 URL
baseUrl: "https://api.example.com",
// 设置连接超时时间为 5000 毫秒(5 秒)
connectTimeout: Duration(milliseconds: 5000),
// 设置接收超时时间为 3000 毫秒(3 秒)
receiveTimeout: Duration(milliseconds: 3000),
),
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🌙 2.3Restful
风格 api:
// get demo
getDemo() async {
try {
Response response = await dio.get(
'/todos',
queryParameters: {
'page': 1,
'limit': 10,
'status': 'completed',
'title': 'foo',
'userId': 1,
'tags': ['foo', 'bar'],
},
options: Options(
headers: {
'Authorization': 'Bearer 1234567890',
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Custom-Header': 'foo',
},)
);
print(response);
} on DioException catch (e) {
print(e.message);
}
}
// post demo
postDemo() async {
try {
Response response = await dio.post('/todos',
data: {
'page': 1,
'limit': 10,
'status': 'completed',
'title': 'foo',
'userId': 1,
'tags': ['foo', 'bar'],
},
options: Options(
headers: {
'Authorization': 'Bearer 1234567890',
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Custom-Header': 'foo',
},
));
print(response);
} on DioException catch (e) {
print(e.message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
🌙 2.4异常处理:
cancel
:请求取消。当请求在完成前被取消时,会触发此错误。connectionTimeout
:连接超时。发生此错误时,表示客户端在与服务器建立连接时超出指定的时间限制。sendTimeout
:发送超时。当请求在发送数据到服务器时超时,会触发此错误。receiveTimeout
:接收超时。当等待服务器响应超出设定的时间限制时,会触发此错误。badResponse
:服务器响应错误。当服务器的响应状态码不在预期的范围内时,会触发此错误。connectionError
:连接错误。当请求由于网络连接问题失败时,会触发此错误。badCertificate
:证书验证失败。这种情况通常发生在 HTTPS 请求中,当服务器的 SSL 证书不被客户端信任时,就会抛出此类型的异常。unknown
:未知错误。当发生未预料到的其他错误时,会使用此类型。
/// 源码定义如下:
/// The exception enumeration indicates what type of exception
/// has happened during requests.
enum DioExceptionType {
/// Caused by a connection timeout.
connectionTimeout,
/// It occurs when url is sent timeout.
sendTimeout,
///It occurs when receiving timeout.
receiveTimeout,
/// Caused by an incorrect certificate as configured by [ValidateCertificate].
badCertificate,
/// The [DioException] was caused by an incorrect status code as configured by
/// [ValidateStatus].
badResponse,
/// When the request is cancelled, dio will throw a error with this type.
cancel,
/// Caused for example by a `xhr.onError` or SocketExceptions.
connectionError,
/// Default error type, Some other [Error]. In this case, you can use the
/// [DioException.error] if it is not null.
unknown,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
代码示例:
handleErrorDemo() async {
try {
Response response = await dio.get("/user?id=123");
} on DioException catch (e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
// 连接超时处理
print('连接超时');
break;
case DioExceptionType.sendTimeout:
// 发送超时处理
print('发送超时');
break;
case DioExceptionType.receiveTimeout:
// 接收超时处理
print('接收超时');
break;
case DioExceptionType.badResponse:
// 服务器响应错误处理
print('服务器响应错误,状态码:${e.response?.statusCode}');
break;
case DioExceptionType.cancel:
// 请求取消处理
print('请求被取消');
break;
case DioExceptionType.connectionError:
// 连接错误处理
print('连接错误');
break;
case DioExceptionType.unknown:
default:
// 其他错误处理
print('未知错误');
break;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
🌙 2.5 Dio拦截器
Dio initDio() {
final dio = Dio();
// 添加拦截器
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// 在请求发送前添加逻辑
// 例如,添加一个自定义的请求头
options.headers["Custom-Header"] = "custom-header";
options.headers["Authorization"] = "Bearer 123456";
// 继续执行请求
return handler.next(options);
},
onResponse: (response, handler) {
// 在响应返回后添加逻辑
// 例如,打印响应数据
print(response.data);
// 继续执行响应
return handler.next(response);
},
onError: (DioException e, handler) {
// 在发生错误时添加逻辑
// 例如,根据错误类型显示不同的错误信息
print(e.message);
// 继续执行错误处理
return handler.next(e);
},
));
return dio;
}
final dio = initDio();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
可见,这个拦截器的配置和axios
配置拦截器非常相似,对前端开发来说,配置拦截器非常简单,而且功能非常强大。
使用该拦截器之后,查看请求头可以看到:
GET /posts/1 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Authorization: Bearer 123456 # 自定义请求头
Connection: keep-alive
Custom-Header: custom-header # 自定义请求头
Host: localhost:5200
If-None-Match: W/"124-yiKdLzqO5gfBrJFrcdJ8Yq0LGnU"
Origin: http://localhost:58278
Referer: http://localhost:58278/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
🌙 3.解决web跨域 (opens new window)
使用 shelf_proxy, 创建文件./lib/server/cors.dart
:
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_proxy/shelf_proxy.dart';
//本地域名
const String localHost = 'localhost';
//本地端口
const int localPort = 5200;
//目标域名
const String targetUrl = 'https://jsonplaceholder.typicode.com';
Future main() async {
var server = await shelf_io.serve(
proxyHandler(targetUrl),
localHost,
localPort,
);
// 添加上跨域的这几个header
server.defaultResponseHeaders.add('Access-Control-Allow-Origin', '*');
server.defaultResponseHeaders.add('Access-Control-Allow-Credentials', true);
print('Serving at http://${server.address.host}:${server.port}');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
执行命令,本地代理成功:
dart ./lib/server/cors.dart
1
页面通过访问http://localhost:5200/todos
, 即可访问到 https://jsonplaceholder.typicode.com/todos 的数据。