ウェブリクエストをカプセル化することには、いくつかの利点があります:
- ヘッダ、公開パラメータ、暗号化ルールなどのリクエストパラメータの統一的な設定を容易にします。
- 簡単なデバッグ、詳細なログ印刷情報
- コードパフォーマンスの最適化、新しいオブジェクトのスパムの回避、グローバルシングレットンの構築
- 必要なレスポンスデータのみを公開することでリクエストステップを簡素化し、不正なレスポンスに対するコールバックを統一します。
- 解析プロセスを簡素化するインターフェース・データのベース・クラス・カプセル化
- 非侵入的で柔軟なリクエストロード設定
ロード自動化のリクエスト
コードを記述することなく、パラメータを渡すだけでLoadingの効果をリクエストに追加できます!
var params = DataHelper.getBaseMap();
params.clear();
params["apikey"] = "0df993c66c0c636e29ecbb5344252a4a";
params["start"] = "0";
params["count"] = "10";
//withLoading省略可能、デフォルトが追加される、より簡潔になる
ResultData res = await HttpManager.getInstance()
.get(Address.TEST_API, params: params, withLoading: true);
明確で包括的なログ印刷
パケットキャプチャを追加設定する必要がなくなり、インターフェイスのデバッグ効率が大幅に向上します!
以下は、主要なソースコードを通してカプセル化のプロセスを紹介したものです。
HttpManager
グローバルシングルトンの構築、リクエストパラメータの設定、汎用GETPOSTの設定、baseUrl切り替えのサポート
import 'package:dio/dio.dart';
import 'package:flutter_net/code.dart';
import 'package:flutter_net/dio_log_interceptor.dart';
import 'package:flutter_net/loading_utils.dart';
import 'response_interceptor.dart';
import 'result_data.dart';
import 'address.dart';
class HttpManager {
static HttpManager _instance = HttpManager._internal();
Dio _dio;
static const CODE_SUCCESS = 200;
static const CODE_TIME_OUT = -1;
factory HttpManager() => _instance;
///一般的なグローバル・シングルトンで、最初に使われたときに初期化される。
HttpManager._internal({String baseUrl}) {
if (null == _dio) {
_dio = new Dio(
new BaseOptions(baseUrl: Address.BASE_URL, connectTimeout: 15000));
_dio.interceptors.add(new DioLogInterceptor());
// _dio.interceptors.add(new PrettyDioLogger());
_dio.interceptors.add(new ResponseInterceptors());
}
}
static HttpManager getInstance({String baseUrl}) {
if (baseUrl == null) {
return _instance._normal();
} else {
return _instance._baseUrl(baseUrl);
}
}
//特定のドメイン名を指定する
HttpManager _baseUrl(String baseUrl) {
if (_dio != null) {
_dio.options.baseUrl = baseUrl;
}
return this;
}
//一般的な要望、デフォルトのドメイン名
HttpManager _normal() {
if (_dio != null) {
if (_dio.options.baseUrl != Address.BASE_URL) {
_dio.options.baseUrl = Address.BASE_URL;
}
}
return this;
}
///一般的なGETリクエスト
get(api, {params, withLoading = true}) async {
if (withLoading) {
LoadingUtils.show();
}
Response response;
try {
response = await _dio.get(api, queryParameters: params);
if (withLoading) {
LoadingUtils.dismiss();
}
} on DioError catch (e) {
if (withLoading) {
LoadingUtils.dismiss();
}
return resultError(e);
}
if (response.data is DioError) {
return resultError(response.data['code']);
}
return response.data;
}
///一般的なPOSTリクエスト
post(api, {params, withLoading = true}) async {
if (withLoading) {
LoadingUtils.show();
}
Response response;
try {
response = await _dio.post(api, data: params);
if (withLoading) {
LoadingUtils.dismiss();
}
} on DioError catch (e) {
if (withLoading) {
LoadingUtils.dismiss();
}
return resultError(e);
}
if (response.data is DioError) {
return resultError(response.data['code']);
}
return response.data;
}
}
ResultData resultError(DioError e) {
Response errorResponse;
if (e.response != null) {
errorResponse = e.response;
} else {
errorResponse = new Response(statusCode: 666);
}
if (e.type == DioErrorType.CONNECT_TIMEOUT ||
e.type == DioErrorType.RECEIVE_TIMEOUT) {
errorResponse.statusCode = Code.NETWORK_TIMEOUT;
}
return new ResultData(
errorResponse.statusMessage, false, errorResponse.statusCode);
}
レスポンス基底クラス
デフォルト200 isSuccessがtrueの場合、レスポンスはresponse.data、dataに代入
class ResultData {
var data;
bool isSuccess;
int code;
var headers;
ResultData(this.data, this.isSuccess, this.code, {this.headers});
}
Api
リクエストの一元管理
class Api {
///サンプルリクエスト
static request(String param) {
var params = DataHelper.getBaseMap();
params['param'] = param;
return HttpManager.getInstance().get(Address.TEST_API, params);
}
}
公開パラメータ、暗号化など
class DataHelper{
static SplayTreeMap getBaseMap() {
var map = new SplayTreeMap<String, dynamic>();
map["platform"] = AppConstants.PLATFORM;
map["system"] = AppConstants.SYSTEM;
map["channel"] = AppConstants.CHANNEL;
map["time"] = new DateTime.now().millisecondsSinceEpoch.toString();
return map;
}
static string2MD5(String data) {
var content = new Utf8Encoder().convert(data);
var digest = md5.convert(content);
return hex.encode(digest.bytes);
}
}
アドレスの設定
便利なアドレス管理
class Address {
static const String TEST_API = "test_api";
}
レスポンスインターセプター
初期データカプセル化のための正しいレスポンスデータのフィルタリング
import 'package:dio/dio.dart';
import 'package:exchange_flutter/common/net/code.dart';
import 'package:flutter/material.dart';
import '../result_data.dart';
class ResponseInterceptors extends InterceptorsWrapper {
@override
onResponse(Response response) {
RequestOptions option = response.request;
try {
if (option.contentType != null &&
option.contentType.primaryType == "text") {
return new ResultData(response.data, true, Code.SUCCESS);
}
///一般的には、エラーメッセージを保持するために200, 500のケースに対処するだけでよい。
if (response.statusCode == 200 || response.statusCode == 201) {
int code = response.data["code"];
if (code == 0) {
return new ResultData(response.data, true, Code.SUCCESS,
headers: response.headers);
} else if (code == 100006 || code == 100007) {
} else {
Fluttertoast.showToast(msg: response.data["msg"]);
return new ResultData(response.data, false, Code.SUCCESS,
headers: response.headers);
}
}
} catch (e) {
print(e.toString() + option.path);
return new ResultData(response.data, false, response.statusCode,
headers: response.headers);
}
return new ResultData(response.data, false, response.statusCode,
headers: response.headers);
}
}
ログインターセプター
リクエスト・パラメータとリターン・パラメータの印刷
import 'package:dio/dio.dart';
///ログ・インターセプター
class DioLogInterceptor extends Interceptor {
@override
Future onRequest(RequestOptions options) async {
String requestStr = "
==================== REQUEST ====================
"
"- URL:
${options.baseUrl + options.path}
"
"- METHOD: ${options.method}
";
requestStr += "- HEADER:
${options.headers.mapToStructureString()}
";
final data = options.data;
if (data != null) {
if (data is Map)
requestStr += "- BODY:
${data.mapToStructureString()}
";
else if (data is FormData) {
final formDataMap = Map()
..addEntries(data.fields)
..addEntries(data.files);
requestStr += "- BODY:
${formDataMap.mapToStructureString()}
";
} else
requestStr += "- BODY:
${data.toString()}
";
}
print(requestStr);
return options;
}
@override
Future onError(DioError err) async {
String errorStr = "
==================== RESPONSE ====================
"
"- URL:
${err.request.baseUrl + err.request.path}
"
"- METHOD: ${err.request.method}
";
errorStr +=
"- HEADER:
${err.response.headers.map.mapToStructureString()}
";
if (err.response != null && err.response.data != null) {
print(' ${err.type.toString()}');
errorStr += "- ERROR:
${_parseResponse(err.response)}
";
} else {
errorStr += "- ERRORTYPE: ${err.type}
";
errorStr += "- MSG: ${err.message}
";
}
print(errorStr);
return err;
}
@override
Future onResponse(Response response) async {
String responseStr =
"
==================== RESPONSE ====================
"
"- URL:
${response.request.uri}
";
responseStr += "- HEADER:
{";
response.headers.forEach(
(key, list) => responseStr += "
" + ""$key" : "$list",");
responseStr += "
}
";
responseStr += "- STATUS: ${response.statusCode}
";
if (response.data != null) {
responseStr += "- BODY:
${_parseResponse(response)}";
}
printWrapped(responseStr);
return response;
}
void printWrapped(String text) {
final pattern = new RegExp('.{1,800}'); // 800 is the size of each chunk
pattern.allMatches(text).forEach((match) => print(match.group(0)));
}
String _parseResponse(Response response) {
String responseStr = "";
var data = response.data;
if (data is Map)
responseStr += data.mapToStructureString();
else if (data is List)
responseStr += data.listToStructureString();
else
responseStr += response.data.toString();
return responseStr;
}
}
extension Map2StringEx on Map {
String mapToStructureString({int indentation = 2}) {
String result = "";
String indentationStr = " " * indentation;
if (true) {
result += "{";
this.forEach((key, value) {
if (value is Map) {
var temp = value.mapToStructureString(indentation: indentation + 2);
result += "
$indentationStr" + ""$key" : $temp,";
} else if (value is List) {
result += "
$indentationStr" +
""$key" : ${value.listToStructureString(indentation: indentation + 2)},";
} else {
result += "
$indentationStr" + ""$key" : "$value",";
}
});
result = result.substring(0, result.length - 1);
result += indentation == 2 ? "
}" : "
${" " * (indentation - 1)}}";
}
return result;
}
}
extension List2StringEx on List {
String listToStructureString({int indentation = 2}) {
String result = "";
String indentationStr = " " * indentation;
if (true) {
result += "$indentationStr[";
this.forEach((value) {
if (value is Map) {
var temp = value.mapToStructureString(indentation: indentation + 2);
result += "
$indentationStr" + ""$temp",";
} else if (value is List) {
result += value.listToStructureString(indentation: indentation + 2);
} else {
result += "
$indentationStr" + ""$value",";
}
});
result = result.substring(0, result.length - 1);
result += "
$indentationStr]";
}
return result;
}
}
サンプルリクエスト
dartのjsonパースにはjson_serializableを使うことが推奨されています!
void request() async {
ResultData res = await Api.request("param");
if (res.isSuccess) {
//リセットを取得する.dataJsonの解析が可能で、これは一般的にエンティティ・クラスを構築するために使用される。
TestBean bean = TestBean.fromMap(res.data);
}else{
//エラーを処理する
}
}
Demo
もしお役に立つと思われましたら、星を付けていただけると幸いです。)