Flutter之网络请求

一,介绍与需求

1.1,介绍

1,http一个可组合的,基于Future的库,用于发出HTTP请求。包含一组高级功能和类,可轻松使用HTTP资源。它与平台无关,可以在命令行和浏览器上使用。

2,Dart的功能强大的Http客户端,支持拦截器,全局配置,FormData,请求取消,文件下载,超时等。

1.2,需求

编写一个 App,最离不开的就是网络请求了。目前Flutter普及率也不是很高,网络请求大致分为如下三种方式:
  1. Dart 原生的网络请求HttpClient
  2. 库 http
  3. Flutter中文网发布的 dio

本文主要介绍后面两种网络请求方式的封装与使用,dart的原生网络请求HttpClient可参考文档通过HttpClient发起HTTP请求

二,网络请求封装

第一步:添加依赖

打开 pubspec.yaml文件,在dependencies下添加如下包:

 http: ^0.12.+
dio: ^3.0.

保存后,一般会自动下载包;如果没有自动下载可在项目根目录下运行如下命令,进行下载:

 flutter packages get

2.1,http请求库

第二步:引入包并创建网络请求类

 import 'package:http/http.dart' as http;
 class NetUtils {

 ...

 }

第三步:get方式请求

 // get请求的封装,传入的两个参数分别是请求URL和请求参数,请求参数以map的形式传入,会在方法体中自动拼接到URL后面
static Future<String> get(String url, {Map<String, String> params}) async {
if (params != null && params.isNotEmpty) {
// 如果参数不为空,则将参数拼接到URL后面
StringBuffer sb = StringBuffer("?");
params.forEach((key, value) {
sb.write("$key" + "=" + "$value" + "&");
});
String paramStr = sb.toString();
paramStr = paramStr.substring(0, paramStr.length - 1);
url += paramStr;
}
http.Response res = await http.get(url, headers: getCommonHeader());
return res.body;
}

第四步:POST方式请求

   // post请求
static Future<String> post(String url, {Map<String, String> params}) async {
http.Response res = await http.post(url, body: params, headers: getCommonHeader());
print(res.statusCode);
return res.body;
}

其他请求方式与post方式类似,这儿就不一一列举其他请求方式了。

第五步:统一传参处理

   static Map<String, String> getCommonHeader() {
Map<String, String> header = Map();
header['yingqi'] = "jackson影琪";
return header;
}

第六步:完整代码

 import 'dart:async';
import 'package:http/http.dart' as http; class NetUtils {
// get请求的封装,传入的两个参数分别是请求URL和请求参数,请求参数以map的形式传入,会在方法体中自动拼接到URL后面
static Future<String> get(String url, {Map<String, String> params}) async {
if (params != null && params.isNotEmpty) {
// 如果参数不为空,则将参数拼接到URL后面
StringBuffer sb = StringBuffer("?");
params.forEach((key, value) {
sb.write("$key" + "=" + "$value" + "&");
});
String paramStr = sb.toString();
paramStr = paramStr.substring(0, paramStr.length - 1);
url += paramStr;
}
http.Response res = await http.get(url, headers: getCommonHeader());
return res.body;
} // post请求
static Future<String> post(String url, {Map<String, String> params}) async {
http.Response res = await http.post(url, body: params, headers: getCommonHeader());
print(res.statusCode);
return res.body;
} // put请求
static Future<String> put(String url, {Map<String, String> params}) async {
http.Response res = await http.put(url, body: params, headers: getCommonHeader());
return res.body;
} static Map<String, String> getCommonHeader() {
Map<String, String> header = Map();
header['yingqi'] = "1";
return header;
} }

2.2,Dio

第二步:引入包并创建网络请求类

 import 'dart:async';
import 'package:dio/dio.dart'; class DioNetUtils { ... }

第三步:初始化Dio

   static final DioNetUtils _singleton = DioNetUtils._init();
static Dio _dio; DioNetUtils._init() {
BaseOptions options = new BaseOptions(
baseUrl: "http://192.168.1.19:8880",
connectTimeout: 1000 * 1,
receiveTimeout: 1000 * 2,
//Http请求头.
headers: {//可统一配置传参
//do something
"version": "1.0.0"
},
//请求的Content-Type,默认值是"application/json; charset=utf-8". 也可以用"application/x-www-form-urlencoded"
// contentType: "application/json; charset=utf-8",
//表示期望以那种格式(方式)接受响应数据。接受4种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
responseType: ResponseType.json,
);
_dio = Dio(options); }

第四步:添加拦截器

  //添加拦截器
_dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
print("请求之前处理");
return options; //continue
}, onResponse: (Response response) {
print("响应之前处理");
print(options);
return response; // continue
}, onError: (DioError e) {
print("错误之前提示");
Response errorInfo = _dealErrorInfo(e);
return errorInfo; //continue
}));

第五步:统一处理错误信息

  _dealErrorInfo(error) {
print(error.type);
// 请求错误处理
Response errorResponse;
if (error.response != null) {
errorResponse = error.response;
} else {
errorResponse = new Response(statusCode: 201);
}
// 请求超时
if (error.type == DioErrorType.CONNECT_TIMEOUT) {
ShowToast.warning("网络请求超时,请稍后重试");
errorResponse.statusCode = ResultCode.CONNECT_TIMEOUT;
}
// 请求连接超时
else if (error.type == DioErrorType.RECEIVE_TIMEOUT) {
ShowToast.warning("网络连接超时,请稍后重试");
errorResponse.statusCode = ResultCode.RECEIVE_TIMEOUT;
}
// 服务器错误
else if (error.type == DioErrorType.RESPONSE) {
ShowToast.warning("服务器繁忙,请稍后重试");
errorResponse.statusCode = ResultCode.RESPONSE;
}
// 一般服务器错误
else {
ShowToast.warning("网络连接不可用,请稍后重试1");
errorResponse.statusCode = ResultCode.DEFAULT;
}
return errorResponse;
}

第六步:GET方式请求

   /// Make http request with options.
/// [method] The request method.
/// [path] The url path.
/// [data] The request data
/// [options] The request options.
/// String 返回 json data .
Future<Map> request<T>(
String path, {
String method = Method.get,
String contentType= "application/json; charset=utf-8",
queryParameters,
Options options,
// CancelToken cancelToken,
}) async {
print('path===' + path);
Response response = await _dio.request(
path,
queryParameters: queryParameters,
options: _checkOptions(method, contentType, options),
// cancelToken: cancelToken,
);
_printHttpLog(response);
if (response.statusCode == 200) {
try {
if (response.data is Map) {
if (response.data["httpCode"] != 200) {
ShowToast.warning(response.data["message"]);
return new Future.error(new DioError(
response: response,
type: DioErrorType.RESPONSE,
));
}
// 由于不同的接口返回的格式不固定不规范,所以需要根据接口格式自定义.
return response.data['data'];
} else {
if (response.data is List) {
Map<String, dynamic> _dataMap = Map();
_dataMap["data"] = response.data;
return _dataMap;
}
}
} catch (e) {
ShowToast.warning("网络连接不可用,请稍后重试");
return new Future.error(new DioError(
response: response,
// message: "data parsing exception...",
type: DioErrorType.RESPONSE,
));
}
}
ShowToast.warning("网络连接不可用,请稍后重试");
return new Future.error(new DioError(
response: response,
type: DioErrorType.RESPONSE,
));
}

第七步:POST方式请求-json传值

  /// Make http request with options.
/// [method] The request method.
/// [path] The url path.
/// [data] The request data
/// [options] The request options.
/// String 返回 json data .
Future<Map> request<T>(
String path, {
String method = Method.get,
String contentType= "application/json; charset=utf-8",
queryParameters,
Options options,
// CancelToken cancelToken,
}) async {
print('path===' + path);
Response response;
if (method == Method.get) {
//GET方式 ... } else {
//除GET的其他方式
var requestData = queryParameters;
response = await _dio.request(
path,
data: requestData,
options: _checkOptions(method, contentType, options),
// cancelToken: cancelToken,
);
} _printHttpLog(response);
if (response.statusCode == 200) { ... } ... }

第八步:POST方式请求-表单传值

       //if (contentType == 'application/x-www-form-urlencoded') {//表单方式
var requestData = new FormData.fromMap({
"name": "jackson影琪",
"age": 25,
});

第九步:请求日志处理

  // print Http Log.
void _printHttpLog(Response response) {
print(!_isDebug);
if (!_isDebug) {
return;
}
try {
print("----------------Http Log Start----------------" +
_getOptionsStr(response.request));
print(response);
print("----------------Http Log end----------------");
} catch (ex) {
print("Http Log" + " error......");
}
} // get Options Str.
String _getOptionsStr(RequestOptions request) {
return "method: " +
request.method +
" baseUrl: " +
request.baseUrl +
" path: " +
request.path;
}

第10步:完整代码

 import 'dart:async';
import 'package:dio/dio.dart';
import 'ShowToastUtils.dart'; class DioNetUtils {
static final DioNetUtils _singleton = DioNetUtils._init();
static Dio _dio; /// 是否是debug模式.
static bool _isDebug = true; /// 打开debug模式.
static void openDebug() {
_isDebug = true;
} DioNetUtils._init() {
BaseOptions options = new BaseOptions(
baseUrl: "http://192.168.1.19:8880",
connectTimeout: 1000 * 1,
receiveTimeout: 1000 * 2,
//Http请求头.
headers: {
//do something
"version": "1.0.0"
},
//请求的Content-Type,默认值是"application/json; charset=utf-8". 也可以用"application/x-www-form-urlencoded"
// contentType: "application/json; charset=utf-8",
//表示期望以那种格式(方式)接受响应数据。接受4种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
responseType: ResponseType.json,
);
_dio = Dio(options);
//添加拦截器
_dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
print("请求之前处理");
return options; //continue
}, onResponse: (Response response) {
print("响应之前处理");
print(options);
return response; // continue
}, onError: (DioError e) {
print("错误之前提示");
Response errorInfo = _dealErrorInfo(e);
return errorInfo; //continue
}));
} factory DioNetUtils() {
return _singleton;
} /// Make http request with options.
/// [method] The request method.
/// [path] The url path.
/// [data] The request data
/// [options] The request options.
/// String 返回 json data .
Future<Map> request<T>(
String path, {
String method = Method.get,
String contentType= "application/json; charset=utf-8",
queryParameters,
Options options,
// CancelToken cancelToken,
}) async {
print('path===' + path);
Response response;
if (method == Method.get) {
//GET方式
response = await _dio.request(
path,
queryParameters: queryParameters,
options: _checkOptions(method, contentType, options),
// cancelToken: cancelToken,
);
} else {
//除GET的其他方式
var requestData;
print(contentType);
if (contentType == 'application/x-www-form-urlencoded') {//表单方式
requestData = new FormData.fromMap({
"name": "jackson影琪",
"age": 25,
});
}else{//json格式
requestData = queryParameters;
}
response = await _dio.request(
path,
data: requestData,
options: _checkOptions(method, contentType, options),
// cancelToken: cancelToken,
);
} _printHttpLog(response);
if (response.statusCode == 200) {
try {
if (response.data is Map) {
if (response.data["httpCode"] != 200) {
ShowToast.warning(response.data["message"]);
return new Future.error(new DioError(
response: response,
type: DioErrorType.RESPONSE,
));
}
// 由于不同的接口返回的格式不固定不规范,所以需要根据接口格式自定义.
return response.data['data'];
} else {
if (response.data is List) {
Map<String, dynamic> _dataMap = Map();
_dataMap["data"] = response.data;
return _dataMap;
}
}
} catch (e) {
ShowToast.warning("网络连接不可用,请稍后重试");
return new Future.error(new DioError(
response: response,
// message: "data parsing exception...",
type: DioErrorType.RESPONSE,
));
}
}
ShowToast.warning("网络连接不可用,请稍后重试");
return new Future.error(new DioError(
response: response,
type: DioErrorType.RESPONSE,
));
} /// check Options.
Options _checkOptions(method, contentType, options) {
if (options == null) {
options = new Options();
}
// if (contentType) {
// //设置请求的类型 json 表单
// options.contentType = contentType;
// }
options.method = method;
return options;
} // print Http Log.
void _printHttpLog(Response response) {
print(!_isDebug);
if (!_isDebug) {
return;
}
try {
print("----------------Http Log Start----------------" +
_getOptionsStr(response.request));
print(response);
print("----------------Http Log end----------------");
} catch (ex) {
print("Http Log" + " error......");
}
} // get Options Str.
String _getOptionsStr(RequestOptions request) {
return "method: " +
request.method +
" baseUrl: " +
request.baseUrl +
" path: " +
request.path;
} // 错误全局处理
_dealErrorInfo(error) {
print(error.type);
// 请求错误处理
Response errorResponse;
if (error.response != null) {
errorResponse = error.response;
} else {
errorResponse = new Response(statusCode: 201);
}
// 请求超时
if (error.type == DioErrorType.CONNECT_TIMEOUT) {
ShowToast.warning("网络请求超时,请稍后重试");
errorResponse.statusCode = ResultCode.CONNECT_TIMEOUT;
}
// 请求连接超时
else if (error.type == DioErrorType.RECEIVE_TIMEOUT) {
ShowToast.warning("网络连接超时,请稍后重试");
errorResponse.statusCode = ResultCode.RECEIVE_TIMEOUT;
}
// 服务器错误
else if (error.type == DioErrorType.RESPONSE) {
ShowToast.warning("服务器繁忙,请稍后重试");
errorResponse.statusCode = ResultCode.RESPONSE;
}
// 一般服务器错误
else {
ShowToast.warning("网络连接不可用,请稍后重试1");
errorResponse.statusCode = ResultCode.DEFAULT;
}
return errorResponse;
}
} abstract class DioCallback<T> {
void onSuccess(T t); void onError(DioError error);
}

** dio网络请求失败的回调错误码 **

 /*
* dio网络请求失败的回调错误码 自定义
*/
class ResultCode {
//正常返回是1
static const SUCCESS = 1; //异常返回是0
static const ERROR = 0; /// When opening url timeout, it occurs.
static const CONNECT_TIMEOUT = -1; ///It occurs when receiving timeout.
static const RECEIVE_TIMEOUT = -2; /// When the server response, but with a incorrect status, such as 404, 503...
static const RESPONSE = -3; /// When the request is cancelled, dio will throw a error with this type.
static const CANCEL = -4; /// read the DioError.error if it is not null.
static const DEFAULT = -5;
}

** dio网络请求方式 **

 /// 请求方法.
class Method {
static const String get = "GET";
static final String post = "POST";
static final String put = "PUT";
static final String head = "HEAD";
static final String delete = "DELETE";
static final String patch = "PATCH";
}

三,接口调用

3.1,http请求库

1,页面调用

  Map<String, String> params = Map();
params['loginCode'] = _unameController.text;
params['password'] = _pwdController.text;
NetUtils.post(ServiceApi.loginAction, params: params).then((data) {
print(ServiceApi.loginAction);
print(data);
}).catchError((e) {
Toast.toast(
context,
msg: '网络请求出错:$e,请稍后重试!',
position: 'top',
bgColor: Color.fromRGBO(130, 0, 0, 1), // Color 提示框背景颜色
textColor: Color.fromRGBO(250, 100, 100, 1), // Color 提示框文字颜色
);
});

3.2,Dio

1,服务接口地址

 import 'dart:async';

 import '../util/DioNetUtils.dart';

 class ServiceNetApi {
///获取用户信息
Future<Map> getSingleDataById(data) async {
return await DioNetUtils().request<String>(
"/**/**/yingqi/**/getSingleDataById",
queryParameters: data,
method:Method.put
);
}
}

2,页面调用

   void getData() async {
Map<String, String> params = Map();
params['Id'] = "123456789";
params['Name'] = "jackson影琪";
await ServiceNetApi().getSingleDataById(params).then((json) {
print('getSingleDataById');
print(json);
}).catchError((e) { });
}

3,返回的结果

Flutter之网络请求-LMLPHP

下一章->待定

04-14 00:14