【转】Http网络请求详解

https://dart-lang.dev/2019/03/03/http/

Http网络请求详解


Http网络请求是一门开发语言里比较常用和重要的功能,主要用于资源访问、接口数据请求和提交、上传下载文件等等操作,Http请求方式主要有:GET、POST、HEAD、PUT、DELETE、TRACE、CONNECT、OPTIONS。本文主要GET和POST这两种常用请求在Flutter中的用法,其中对POST将进行着重讲解。Flutter的Http网络请求的实现主要分为三种:io.dart里的HttpClient实现、Dart原生http请求库实现、第三方库实现。后面将会给大家详细讲解这几种区别和特点及前两种的使用方法。接下来,我们就开始Flutter的Http网络请求详解吧。本文将主要介绍:

  • 简单介绍这几种Http请求方式
  • Flutter三种Http网络请求实现的区别和特点
  • HttpClient实现Http网络请求
  • Dart原生http请求库实现Http网络请求
  • 第三方库的推荐

1. Http的请求方式简介

Http网络请求方式就是描述了客户端想对指定的资源或服务器所要执行的操作。开头简介里介绍过,Http网络请求是一门开发语言里比较常用和重要的功能,主要用于资源访问、接口数据请求和提交、上传下载文件等等操作。其中主要的请求方式有:GET、POST、HEAD、PUT、DELETE、TRACE、CONNECT、OPTIONS这八种。接下来先简单介绍它们的特点和作用。

1.1 GET请求方式

从GET这个单词上也可以看出,它主要是执行获取资源操作的,例如通过URL从服务器获取返回的资源,其中GET可以把请求的一些参数信息拼接在URL上,传递给服务器,由服务器端进行参数信息解析,然后返回相应的资源给请求者。注意:GET请求拼接的URL数据大小和长度是有最大限制的,传输的数据量一般限制在2KB。

1.2 POST请求方式

POST主要是执行提交信息、传输信息的操作,POST请求的可以携带很多的数据,而且格式不限。如JSON、XML、文本等等都支持。并且POST传递的一些数据和参数不是直接拼接在URL后的,而是放在Http请求Body里,相对GET来说比较安全。并且传递的数据大小和格式是无限制的。POST请求方式是比较重要和常用的一种,POST请求包含两部分:请求头(header)和请求体(body)。POST请求常见的请求体(body)有三种传输内容类型Content-type:application/x-www-form-urlencoded、application/json、multipart/form-data,当然还有其他的几种,不过不常用,常用的就是这三种。

1.3 HEAD请求方式

HEAD主要是执行给请求的客户端返回头信息,而不返回Body主体内容。和GET方式类似,只不过GET方式有Body实体返回,而HEAD只返回头信息,无Body实体内容返回。主要是用于确认URL的有效性、资源更新的日期时间、查看服务器状态等等,对于有这方面需求的请求来说,比较不占用资源。

1.4 PUT请求方式

PUT主要是执行传输文件操作,类似于FTP的文件上传一样,请求里包含文件内容,并将此文件保存到URI指定的服务器位置。和POST方式的主要区别是:PUT请求方式如果前后两个请求相同,则后一个请求会把前一个请求覆盖掉,实现了PUT方式的修改资源;而POST请求方式如果前后两个请求相同,则后一个请求不会把前一个请求覆盖掉,实现了POST的增加资源。

1.5 DELETE请求方式

DELETE主要是执行告诉服务器想要删除的资源,执行删除指定资源操作。

1.6 OPTIONS请求方式

OPTIONS主要是执行查询针对所要请求的URI资源服务器所支持的请求方式,也就是获取这个URI所支持客户端提交给服务器端的请求方式有哪些。

1.7 TRACE请求方式

TRACE主要是执行追踪传输路径的操作,例如,我们发起了一个Http请求,在这个过程中这个请求可能会经过很多个路径和过程,TRACE就是告诉服务器在收到请求后,返回一条响应信息,将它收到的原始Http请求信息返回给客户端,这样就可以确认在Http传输过程中请求是否被修改过。

1.8 CONNECT请求方式

CONNECT主要就是执行连接代理操作,例如“翻墙”。客户端通过CONNECT方式与服务器建立通信隧道,进行TCP通信。主要通过SSL和TLS安全传输数据。CONNECT的作用就是告诉服务器让它代替客户端去请求访问某个资源,然后再将数据返回给客户端,相当于一个媒介中转。

2. Flutter Http网络请求实现的区别和特点

介绍完了Http几种请求方式,我们看下Flutter中的Http网络请求的实现方式。Flutter的Http网络请求的实现主要分为三种:io.dart里的HttpClient实现、Dart原生http请求库实现、第三方库实现。

我们首先看下第一种:io.dart里的HttpClient实现。

io.dart里的HttpClient实现的Http网络请求主要是实现了基本的网络请求,复杂一些的网络请求还无法完成。例如POST里的其他几种Body请求体传输内容类型部分还无法支持,multipart/form-data这个类型传输还不支持。所以如果你的一些Http网络请求可以通过io.dart里的HttpClient实现的话,用这个也可以完成要求。
那么接下来我们就看下io.dart里的HttpClient实现的Http网络请求实现步骤。

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">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
90
91
92
93
94
95
96
</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 600px;">import 'dart:convert';
import 'dart:io';

class IOHttpUtils {
//创建HttpClient
HttpClient _httpClient = HttpClient();

//要用async关键字异步请求
getHttpClient() async {
_httpClient
.get('https://abc.com', 8090, '/path1')
.then((HttpClientRequest request) {
//在这里可以对request请求添加headers操作,写入请求对象数据等等
// Then call close.
return request.close();
}).then((HttpClientResponse response) {
// 处理response响应
if (response.statusCode == 200) {
response.transform(utf8.decoder).join().then((String string) {
print(string);
});
} else {
print("error");
}
});
}

getUrlHttpClient() async {
var url = "https://abc.com:8090/path1";
_httpClient.getUrl(Uri.parse(url)).then((HttpClientRequest request) {
// Optionally set up headers...
// Optionally write to the request object...
// Then call close.
return request.close();
}).then((HttpClientResponse response) {
// Process the response.
if (response.statusCode == 200) {
response.transform(utf8.decoder).join().then((String string) {
print(string);
});
} else {
print("error");
}
});
}

//进行POST请求
postHttpClient() async {
_httpClient
.post('https://abc.com', 8090, '/path2')
.then((HttpClientRequest request) {
//这里添加POST请求Body的ContentType和内容
//这个是application/json数据类型的传输方式
request.headers.contentType = ContentType("application", "json");
request.write("{"name":"value1","pwd":"value2"}");
return request.close();
}).then((HttpClientResponse response) {
// Process the response.
if (response.statusCode == 200) {
response.transform(utf8.decoder).join().then((String string) {
print(string);
});
} else {
print("error");
}
});
}

postUrlHttpClient() async {
var url = "https://abc.com:8090/path2";
_httpClient.postUrl(Uri.parse(url)).then((HttpClientRequest request) {
//这里添加POST请求Body的ContentType和内容
//这个是application/x-www-form-urlencoded数据类型的传输方式
request.headers.contentType =
ContentType("application", "x-www-form-urlencoded");
request.write("name='value1'&pwd='value2'");
return request.close();
}).then((HttpClientResponse response) {
// Process the response.
if (response.statusCode == 200) {
response.transform(utf8.decoder).join().then((String string) {
print(string);
});
} else {
print("error");
}
});
}

///其余的HEAD、PUT、DELETE请求用法类似,大同小异,大家可以自己试一下
///在Widget里请求成功数据后,使用setState来更新内容和状态即可
///setState(() {
/// ...
/// });

}
</pre>

|

第二种:Dart原生http请求库实现。

这里推荐这种方式使用,毕竟Dart原生的http请求库支持的Http请求比较全面,比较复杂的请求都可以实现,如上传和下载文件等等操作。

Dart目前官方的仓库里有大量的三方库和官方库,引用也非常的方便,Dart PUB官方地址为:https://pub.dartlang.org。
打开后如下图所示:

使用Dart原生http库,我们首先需要在Dart PUB或官方Github里把相关的http库引用下来。
在Dart PUB里搜索http,便可以查找到我们的http库,根据说明进行引用和使用即可。
http库官方Github库地址为:https://github.com/dart-lang/http

点击Installing,查看引用方法进行引用即可。

在项目的pubspec.yaml配置文件里加入引用:

完毕,这样就可以在dart文件类里直接import使用了。接下来给一个完整的使用例子:

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 647px;">import 'dart:convert';
import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';

class DartHttpUtils {
//创建client实例
var _client = http.Client();

//发送GET请求
getClient() async {
var url = "https://abc.com:8090/path1?name=abc&pwd=123";
_client.get(url).then((http.Response response) {
//处理响应信息
if (response.statusCode == 200) {
print(response.body);
} else {
print('error');
}
});
}

//发送POST请求,application/x-www-form-urlencoded
postUrlencodedClient() async {
var url = "https://abc.com:8090/path2";
//设置header
Map<String, String> headersMap = new Map();
headersMap["content-type"] = "application/x-www-form-urlencoded";
//设置body参数
Map<String, String> bodyParams = new Map();
bodyParams["name"] = "value1";
bodyParams["pwd"] = "value2";
_client
.post(url, headers: headersMap, body: bodyParams, encoding: Utf8Codec())
.then((http.Response response) {
if (response.statusCode == 200) {
print(response.body);
} else {
print('error');
}
}).catchError((error) {
print('error');
});
}

//发送POST请求,application/json
postJsonClient() async {
var url = "https://abc.com:8090/path3";
Map<String, String> headersMap = new Map();
headersMap["content-type"] = ContentType.json.toString();
Map<String, String> bodyParams = new Map();
bodyParams["name"] = "value1";
bodyParams["pwd"] = "value2";
_client
.post(url,
headers: headersMap,
body: jsonEncode(bodyParams),
encoding: Utf8Codec())
.then((http.Response response) {
if (response.statusCode == 200) {
print(response.body);
} else {
print('error');
}
}).catchError((error) {
print('error');
});
}

// 发送POST请求,multipart/form-data
postFormDataClient() async {
var url = "https://abc.com:8090/path4";
var client = new http.MultipartRequest("post", Uri.parse(url));
client.fields["name"] = "value1";
client.fields["pwd"] = "value2";
client.send().then((http.StreamedResponse response) {
if (response.statusCode == 200) {
response.stream.transform(utf8.decoder).join().then((String string) {
print(string);
});
} else {
print('error');
}
}).catchError((error) {
print('error');
});
}

// 发送POST请求,multipart/form-data,上传文件
postFileClient() async {
var url = "https://abc.com:8090/path5";
var client = new http.MultipartRequest("post", Uri.parse(url));
http.MultipartFile.fromPath('file', 'sdcard/img.png',
filename: 'img.png', contentType: MediaType('image', 'png'))
.then((http.MultipartFile file) {
client.files.add(file);
client.fields["description"] = "descriptiondescription";
client.send().then((http.StreamedResponse response) {
if (response.statusCode == 200) {
response.stream.transform(utf8.decoder).join().then((String string) {
print(string);
});
} else {
response.stream.transform(utf8.decoder).join().then((String string) {
print(string);
});
}
}).catchError((error) {
print(error);
});
});
}
///其余的HEAD、PUT、DELETE请求用法类似,大同小异,大家可以自己试一下
///在Widget里请求成功数据后,使用setState来更新内容和状态即可
///setState(() {
/// ...
/// });
}
</pre>

|

第三种:第三方库实现。

Flutter第三方库有很多可以实现Http网络请求,例如国内开发者开发的dio库,dio支持多个文件上传、文件下载、并发请求等复杂的操作。在Dart PUB上可以搜索dio。

在项目的pubspec.yaml配置文件里加入引用:

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">1
2
</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 130px;">dependencies:
dio: ^2.0.14
</pre>

|

这样就可以引用dio的API库来实现Http网络请求了。给一个完整的dio用法例子:

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 615px;">import 'dart:io';

import 'package:dio/dio.dart';

class DartHttpUtils {
//配置dio,通过BaseOptions
Dio _dio = Dio(BaseOptions(
baseUrl: "https://abc.com:8090/",
connectTimeout: 5000,
receiveTimeout: 5000));

//dio的GET请求
getDio() async {
var url = "/path1?name=abc&pwd=123";
_dio.get(url).then((Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
});
}

getUriDio() async {
var url = "/path1?name=abc&pwd=123";
_dio.getUri(Uri.parse(url)).then((Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
}).catchError((error) {
print(error.toString());
});
}

//dio的GET请求,通过queryParameters配置传递参数
getParametersDio() async {
var url = "/path1";
_dio.get(url, queryParameters: {"name": 'abc', "pwd": 123}).then(
(Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
}).catchError((error) {
print(error.toString());
});
}

//发送POST请求,application/x-www-form-urlencoded
postUrlencodedDio() async {
var url = "/path2";
_dio
.post(url,
data: {"name": 'value1', "pwd": 123},
options: Options(
contentType:
ContentType.parse("application/x-www-form-urlencoded")))
.then((Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
}).catchError((error) {
print(error.toString());
});
}

//发送POST请求,application/json
postJsonDio() async {
var url = "/path3";
_dio
.post(url,
data: {"name": 'value1', "pwd": 123},
options: Options(contentType: ContentType.json))
.then((Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
}).catchError((error) {
print(error.toString());
});
}

// 发送POST请求,multipart/form-data
postFormDataDio() async {
var url = "/path4";
FormData _formData = FormData.from({
"name": "value1",
"pwd": 123,
});
_dio.post(url, data: _formData).then((Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
}).catchError((error) {
print(error.toString());
});
}

// 发送POST请求,multipart/form-data,上传文件
postFileDio() async {
var url = "/path5";
FormData _formData = FormData.from({
"description": "descriptiondescription",
"file": UploadFileInfo(File("./example/upload.txt"), "upload.txt")
});
_dio.post(url, data: _formData).then((Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
}).catchError((error) {
print(error.toString());
});
}

//dio下载文件
downloadFileDio() {
var urlPath = "https://abc.com:8090/";
var savePath = "./abc.html";
_dio.download(urlPath, savePath).then((Response response) {
if (response.statusCode == 200) {
print(response.data.toString());
}
}).catchError((error) {
print(error.toString());
});
}

///其余的HEAD、PUT、DELETE请求用法类似,大同小异,大家可以自己试一下
///在Widget里请求成功数据后,使用setState来更新内容和状态即可
///setState(() {
/// ...
/// });
}
</pre>

|

好了,Flutter的Http网络请求详解就为大家讲解到这里了,有疑问可以及时反馈。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容