之所以封装,千言万语汇成一句话:方便使用。
Dio 相关
dio 是一个强大的 Dart Http 请求库,支持 Restful API、FormData、拦截器、请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器等...
dependencies:
dio: ^3.0.9
复制代码
import 'package:dio/dio.dart';
void getHttp() async {
try {
Response response = await Dio().get("http://www.baidu.com");
print(response);
} catch (e) {
print(e);
}
}
复制代码
<br>
封装开始
class HttpUtil {
static HttpUtil instance;
Dio dio;
BaseOptions options;
CancelToken cancelToken = new CancelToken();
static HttpUtil getInstance() {
if (null == instance) instance = new HttpUtil();
return instance;
}
}
复制代码
/*
* config it and create
*/
HttpUtil() {
//BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
options = new BaseOptions(
//请求基地址,可以包含子路径
baseUrl: "http://www.google.com",
//连接服务器超时时间,单位是毫秒.
connectTimeout: 10000,
//响应流上前后两次接受到数据的间隔,单位为毫秒。
receiveTimeout: 5000,
//Http请求头.
headers: {
//do something
"version": "1.0.0"
},
//请求的Content-Type,默认值是"application/json; charset=utf-8",Headers.formUrlEncodedContentType会自动编码请求体.
contentType: Headers.formUrlEncodedContentType,
//表示期望以那种格式(方式)接受响应数据。接受4种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
responseType: ResponseType.json,
);
dio = new Dio(options);
}
复制代码
配置一目了然,但是有一个潜在的问题,细心的同学可能会发现,baseUrl 的参数是固定的,在实际开发中请求两个及以上的域名地址是有很大可能的,所以我们怎么动态更换 baseUrl 呢?
这里的配置我们用的是 BaseOptions,其实还有 Options、RequestOptions, 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
BaseOptions 基类请求配置 Options 单次请求配置 RequestOptions 实际请求配置
所以,我们可以在需要的地方创建 RequestOptions,然后代替或覆盖 BaseOptions 加到请求参数中
比如:
RequestOptions requestOptions=new RequestOptions(
baseUrl: "http://www.google.com/aa/bb/cc/"
);
复制代码
<br>
get 请求
/*
* get请求
*/
get(url, {data, options, cancelToken}) async {
Response response;
try {
response = await dio.get(url, queryParameters: data, options: options, cancelToken: cancelToken);
print('get success---------${response.statusCode}');
print('get success---------${response.data}');
// response.data; 响应体
// response.headers; 响应头
// response.request; 请求体
// response.statusCode; 状态码
} on DioError catch (e) {
print('get error---------$e');
formatError(e);
}
return response.data;
}
复制代码
url:请求地址 data:请求参数 options:请求配置 cancelToken:取消标识
<br>
post 请求
/*
* post请求
*/
post(url, {data, options, cancelToken}) async {
Response response;
try {
response = await dio.post(url, queryParameters: data, options: options, cancelToken: cancelToken);
print('post success---------${response.data}');
} on DioError catch (e) {
print('post error---------$e');
formatError(e);
}
return response.data;
}
复制代码
post Form 表单
跟一般的 post 请求大同小异,只是 data 的变化而已
FormData formData = FormData.from({
"name": "wendux",
"age": 25,
});
response = await dio.post("/info", data: formData);
复制代码
创建 FormData,然后代替原来的 data 即可
<br>
异常处理
出现异常的时候,当然是知道的越清晰越仔细越好,也越容易处理
/*
* error统一处理
*/
void formatError(DioError e) {
if (e.type == DioErrorType.CONNECT_TIMEOUT) {
// It occurs when url is opened timeout.
print("连接超时");
} else if (e.type == DioErrorType.SEND_TIMEOUT) {
// It occurs when url is sent timeout.
print("请求超时");
} else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
//It occurs when receiving timeout
print("响应超时");
} else if (e.type == DioErrorType.RESPONSE) {
// When the server response, but with a incorrect status, such as 404, 503...
print("出现异常");
} else if (e.type == DioErrorType.CANCEL) {
// When the request is cancelled, dio will throw a error with this type.
print("请求取消");
} else {
//DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
print("未知错误");
}
}
复制代码
<br>
Cookie 管理
Cookie 管理是 http 中绕不开的话题,要保持回话持久,就要 cookie 持久化
dependencies:
dio_cookie_manager: ^1.0.0
复制代码
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
复制代码
//Cookie管理
dio.interceptors.add(CookieManager(CookieJar()));
复制代码
<br>
添加拦截器
拦截器可以在请求之前、响应之前、error 之前做一些预处理
dio = new Dio(options);
//添加拦截器
dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
print("请求之前");
// Do something before request is sent
return options; //continue
}, onResponse: (Response response) {
print("响应之前");
// Do something with response data
return response; // continue
}, onError: (DioError e) {
print("错误之前");
// Do something with response error
return e; //continue
}));
复制代码
<br>
下载文件
下载文件也是经常会用到的,比如下载图片,或者是更新
/*
* 下载文件
*/
downloadFile(urlPath, savePath) async {
Response response;
try {
response = await dio.download(urlPath, savePath,onReceiveProgress: (int count, int total){
//进度
print("$count $total");
});
print('downloadFile success---------${response.data}');
} on DioError catch (e) {
print('downloadFile error---------$e');
formatError(e);
}
return response.data;
}
复制代码
<br>
取消请求
/*
* 取消请求
*
* 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
* 所以参数可选
*/
void cancelRequests(CancelToken token) {
token.cancel("cancelled");
}
复制代码
<br>
Https 证书校验
有两种方法可以校验 https 证书,假设我们的后台服务使用的是自签名证书,证书格式是 PEM 格式,我们将证书的内容保存在本地字符串中,那么我们的校验逻辑如下:
String PEM="XXXXX"; // certificate content
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
client.badCertificateCallback=(X509Certificate cert, String host, int port){
if(cert.pem==PEM){ // Verify the certificate
return true;
}
return false;
};
};
复制代码
X509Certificate 是证书的标准格式,包含了证书除私钥外所有信息,读者可以自行查阅文档。另外,上面的示例没有校验 host,是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了(而不是中间人),host 验证通常是为了防止证书和域名不匹配。
对于自签名的证书,我们也可以将其添加到本地证书信任链中,这样证书验证时就会自动通过,而不会再走到 badCertificateCallback 回调中:
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
SecurityContext sc = new SecurityContext();
//file is the path of certificate
sc.setTrustedCertificates(file);
HttpClient httpClient = new HttpClient(context: sc);
return httpClient;
};
复制代码
注意,通过 setTrustedCertificates()设置的证书格式必须为 PEM 或 PKCS12,如果证书格式为 PKCS12,则需将证书密码传入,这样则会在代码中暴露证书密码,所以客户端证书校验不建议使用 PKCS12 格式的证书。
<br>
调用示例
var response = await HttpUtil().get("http://www.baidu.com");
print(response.toString());
复制代码
json 解析查看:Flutter Json自动解析之FlutterJsonBeanFactory
<br>
完整代码
github:https://github.com/yechaoa/wanandroid_flutter
<br>
觉得有用点个赞呗 ^ _ ^
<br><br>
评论