flutter 虽然有原生的 HttpClient
,package:http
对开发会更友好,这是官方推荐的网络请求模块。
安装 http package
fluter pub add http 复制代码
在 android 文件夹下的 AndroidManifest.xml 文件,添加网络权限,
<uses-permission android:name="android.permission.INTERNET" /> 复制代码
构造 URI
因为 http package 需要 Uri,所以先了解一下。
uri 是 Uniform Resource Identifier的缩写,即是一个用于标识某一互联网资源名称的字符串。url 是 uri 的子集。uri 的表示范围更加广泛,可以包括但不限于 url 资源。
Uri( String? scheme, String? userInfo, String? host, int? port, String? path, Iterable<String>? pathSegments, String? query, Map<String, dynamic /*String|Iterable<String>*/ >? queryParameters, String? fragment ); 复制代码
path 和 pathSegments 都是用来生成 URI path,不同的是一个是直接给出,另一个分段给出,给出一个即可。实际上 path 会更常用。
query 和 queryParameters 都是用来生成 URI query,不同的是 query 直接给出,queryParameters 分段给出。两个给出一个即可,如果没有,可以全部为空。实际上 queryParameters 会更常用。
queryParameters 的 key 是一个字符串,value 部分有两种形式。
{"id":"1"} Map<String,String> {"ids":["1","2","3"]} Map<String,Iterable<String> 复制代码
fragment 是锚点。userInfo 可以忽略,一般不用到。
GET 请求
最简单的请求只需要一个参数 uri。
http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')); 复制代码
还可以添加 header 比如 cookie
http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),{ 'cookie':'token=AC4REWD' }); 复制代码
添加 header 的格式 Map<String, String> ,key 和 value都是字符串。
HEAD 请求
和 GET 请求用法相同。不同的是返回的内容没有 body。
POST 请求
最简单的请求需要两个参数 uri 和 body,因为如果只有 uri,用 GET 就好了。我们常用的 body 有两种格式
body
是一个字符串。比如 http 请求的请求体 是 "hello",请求的 content-type 默认为 "text/plain"。
http.post(Uri.parse('https://juejin.cn'),body:'hello'); 复制代码
body
是一个 Map, 请求的 content-type 是 "application/x-www-form-urlencoded"
,不能手动修改。
http.post(Uri.parse('https://juejin.cn'),body:{'content':'hello','from':'faraway'}); 复制代码
还可以加 header,只要不在第一个位置就行。
http.post(Uri.parse('https://juejin.cn'),body:'hello',header:{'cookie':'token=AC4REWD'}); 复制代码
PUT 请求
和 POST 请求用法相同,但是对资源产生的影响不同。POST 产生的影响 非 幂等 ,PUT 产生的影响是 幂等 的。
服务端可能不支持 PUT 请求,所以更多时候,我们是用 POST 请求代替。
DELETE 请求
DELETE 请求的目的很明确,删除目标资源。多次请求产生的影响不变,所以 DELETE 请求也是幂等 的。
服务端可能不支持 DELETE 请求,所以更多时候,我们是用 POST 请求代替。
上传文件
前面讲的 GET,POST,PUT,DELETE 的请求包含的内容只是文本,当请求中有二进制内容时,我们用 MultipartRequest。 可以一次上传多个 Filds,可以有文本,也可以有文件。MultipartRequest 请求会自动设置 Content-Type header 为 multipart/form-data
,并且不能被用户修改。
var uri = Uri.https('example.com', 'create'); var request = http.MultipartRequest('POST', uri) ..fields['user'] = 'nweiz@google.com' ..files.add(await http.MultipartFile.fromPath( 'package', 'build/package.tar.gz', contentType: MediaType('application', 'x-tar'))); var response = await request.send(); if (response.statusCode == 200) print('Uploaded!'); 复制代码
field 相当于 key,服务端通过field 拿到需要的内容。
fields['user'] = 'nweiz@google.com'
field 是 user,内容是 nweiz@google.com
fromPath( 'package', 'build/package.tar.gz', contentType: MediaType('application', 'x-tar'))
,field 是 package,内容是文件 build/package.tar.gz 中的内容。contentType 是可选的。
设置超时
设置超时很简单,只需要加上在请求的后面加上 timeout。拿 GET 请求举例:
http.get(Uri.parse('https://juejin.cn').timeout(Duration(microseconds: 1));; 复制代码
为了让超时一定发生,设置超时时间为 1 微秒。超时后会抛出 TimeoutException。
timeout 方法返回一个新的 future,如果原来的 future 按时 complete,那么 新 future 返回原 future 的 value。如果失败,抛 TimeoutException。
timoute 方法 还可以有第二个参数 onTimeout,如果设置了 onTimeout,返回
T
orFuture<T>
手动把 json 转换成 Dart 对象
通过 http package 拿到的数据还不能用。
先转成 json,再转成 dart 对象。
import 'dart:convert'; final response = await http .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')); if (response.statusCode == 200) { return Album.fromJson(jsonDecode(response.body)); } else { throw Exception('Failed to load album'); } class Album { final int userId; final int id; final String title; const Album({ required this.userId, required this.id, required this.title, }); factory Album.fromJson(Map<String, dynamic> json) { return Album( userId: json['userId'], id: json['id'], title: json['title'], ); } } 复制代码
用工具自动转 json 为 dart 对象
在线转 javiercbk.github.io/json_to_dar…
用在线转的方式比较灵活,先在线转出一个大致的类,然后再做少量修改。
dart 对象如何转成 json String?
只要给 类 加一个 toJson
方法,用 jsonEncode
方法就可以把一个对象转成 String
class Fruit { toJson(){ return 'fruite'; } } jsonEncode(Fruite()) // 输出:fruite 复制代码
对于 toJson
返回的值具体是什么,jsonEncode
是不会检查的,最后都会转成 String。比如
class Fruit { toJson(){ return {"apple":1} } } jsonEncode(Fruite()) // 输出:{"apple":1} 复制代码
class Fruit { toJson(){ return [1,2,3]; } } jsonEncode(Fruite()) // 输出:[1,2,3] 复制代码
一般情况下我们都是返回 一个 Map
,转成 String 后,正好是一个 json String。