dart版本:(stable) 2.2.2(空安全)
connectivity:^3.0.6
shared_preferences:^2.0.6
注:我是抄后改成适合自己的,不过网上太多一模一样的,不知道谁才是原创,在此感谢原创作者。
现有功能。
就看http,再分别一个个看,大概能看懂了。
代码里有些做了解释,其他有些我也是抄的,也不是很懂哈哈哈,就说了,直接上代码。
api_response.dart
import 'app_exceptions.dart';
class ApiResponse<T> implements Exception {
Status status;
T? data;
AppException? exception;
ApiResponse.completed(this.data) : status = Status.COMPLETED;
ApiResponse.error(this.exception) : status = Status.ERROR;
@override
String toString() {
return "Status : $status \n Message : $exception \n Data : $data";
}
}
enum Status { COMPLETED, ERROR }
app_exceptions.dart
import 'package:dio/dio.dart';
/// 自定义异常
class AppException implements Exception {
final String? _message;
final int? _code;
AppException([
this._code,
this._message,
]);
String toString() {
return "$_message($_code)";
}
factory AppException.create(DioError error) {
switch (error.type) {
case DioErrorType.cancel:
{
return BadRequestException(-1, "请求取消");
}
break;
case DioErrorType.connectTimeout:
{
return BadRequestException(-1, "连接超时");
}
break;
case DioErrorType.sendTimeout:
{
return BadRequestException(-1, "请求超时");
}
break;
case DioErrorType.receiveTimeout:
{
return BadRequestException(-1, "响应超时");
}
break;
case DioErrorType.response:
{
try {
int? errCode = error.response?.statusCode!;
// String errMsg = error.response.statusMessage;
// return ErrorEntity(code: errCode, message: errMsg);
switch (errCode) {
case 400:
{
return BadRequestException(errCode, "请求语法错误");
}
break;
case 401:
{
return UnauthorisedException(errCode!, "没有权限");
}
break;
case 403:
{
return UnauthorisedException(errCode!, "服务器拒绝执行");
}
break;
case 404:
{
return UnauthorisedException(errCode!, "无法连接服务器");
}
break;
case 405:
{
return UnauthorisedException(errCode!, "请求方法被禁止");
}
break;
case 500:
{
return UnauthorisedException(errCode!, "服务器内部错误");
}
break;
case 502:
{
return UnauthorisedException(errCode!, "无效的请求");
}
break;
case 503:
{
return UnauthorisedException(errCode!, "服务器挂了");
}
break;
case 505:
{
return UnauthorisedException(errCode!, "不支持HTTP协议请求");
}
break;
default:
{
// return ErrorEntity(code: errCode, message: "未知错误");
return AppException(
errCode, error.response?.statusMessage ?? '');
}
}
} on Exception catch (_) {
return AppException(-1, "未知错误");
}
}
break;
default:
{
return AppException(-1, error.message);
}
}
}
}
/// 请求错误
class BadRequestException extends AppException {
BadRequestException([int? code, String? message]) : super(code!, message!);
}
/// 未认证异常
class UnauthorisedException extends AppException {
UnauthorisedException([int code = -1, String message = ''])
: super(code, message);
}
cache.dart
// 是否启用缓存
const CACHE_ENABLE = true;
// 缓存的最长时间,单位(秒)
const CACHE_MAXAGE = 1000;
// 最大缓存数
const CACHE_MAXCOUNT = 100;
connections_interceptor.dart
import 'package:connectivity/connectivity.dart';
import 'package:dio/dio.dart';
class ConnectsInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
// TODO: implement onRequest
super.onRequest(options, handler);
_request();
}
//不知道是不是这样写,网络的
_request() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
// I am connected to a mobile network.
} else if (connectivityResult == ConnectivityResult.wifi) {
// I am connected to a wifi network.
} else {
print('没有网络');
//在这里加一个错误弹窗
}
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
// TODO: implement onResponse
super.onResponse(response, handler);
}
@override
void onError(DioError err, ErrorInterceptorHandler handler) {
// TODO: implement onError
super.onError(err, handler);
}
}
http.dart
import 'dart:io';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:pp/MyHttp/connections_interceptor.dart';
import 'package:pp/MyHttp/net_cache.dart';
import 'package:pp/MyHttp/proxy.dart';
import 'package:pp/MyHttp/request_interceptor.dart';
class Https {
///超时时间
static const int CONNECT_TIMEOUT = 10000;
static const int RECEIVE_TIMEOUT = 10000;
static Https instance = Https._internal();
factory Https() => instance;
Dio dio = Dio();
CancelToken _cancelToken = new CancelToken();
Https._internal() {
dio.options
..baseUrl = 'https://mobile.pku-hit.com/smc/'
..connectTimeout = CONNECT_TIMEOUT
..receiveTimeout = RECEIVE_TIMEOUT
..validateStatus = (int? status) {
return status != null && status > 0;
}
..headers = {};
dio.interceptors.add(RequestInterceptor()); //自定义拦截
dio.interceptors.add(ConnectsInterceptor());//拦截网络
dio.interceptors.add(LogInterceptor()); //打开日志
dio.interceptors.add(NetCacheInterceptor()); //缓存
// 在调试模式下需要抓包调试,所以我们使用代理,并禁用HTTPS证书校验
if (PROXY_ENABLE) {
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
client.findProxy = (uri) {
return "PROXY $PROXY_IP:$PROXY_PORT";
};
//代理工具会提供一个抓包的自签名证书,会通不过证书校验,所以我们禁用证书校验
client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
};
}
}
void init(//这个在main或者初始化的时候先调用一下
{String? baseUrl,
int? connectTimeout,
int? receiveTimeout,
List<Interceptor>? interceptors}) {
dio.options = dio.options.copyWith(
baseUrl: baseUrl,
connectTimeout: connectTimeout,
receiveTimeout: receiveTimeout,
);
if (interceptors != null && interceptors.isNotEmpty) {
dio.interceptors.addAll(interceptors);
}
}
/// 设置headers
void setHeaders(Map<String, dynamic> map) {
dio.options.headers.addAll(map);
}
/*
* 取消请求
*
* 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
* 所以参数可选
*/
void cancelRequests({CancelToken? token}) {
token ?? _cancelToken.cancel("cancelled");
}
/// restful get 操作
Future get(
String path, {
Map<String, dynamic>? params,
Options? options,
CancelToken? cancelToken,
bool refresh = false,
bool noCache = true,
String? cacheKey,
bool cacheDisk = false,
}) async {
Options requestOptions = options ?? Options();
requestOptions = requestOptions.copyWith(extra: {
"refresh": refresh,
"noCache": noCache,
"cacheKey": cacheKey,
"cacheDisk": cacheDisk,
});
Response response;
response = await dio.get(path,
queryParameters: params,
options: requestOptions,
cancelToken: cancelToken ?? _cancelToken);
return response.data;
}
/// restful post 操作
Future post(
String path, {
Map<String, dynamic>? params,
data,
Options? options,
CancelToken? cancelToken,
}) async {
Options requestOptions = options ?? Options();
var response = await dio.post(path,
data: data,
queryParameters: params,
options: requestOptions,
cancelToken: cancelToken ?? _cancelToken);
return response.data;
}
/// restful put 操作
Future put(
String path, {
data,
Map<String, dynamic>? params,
Options? options,
CancelToken? cancelToken,
}) async {
Options requestOptions = options ?? Options();
var response = await dio.put(path,
data: data,
queryParameters: params,
options: requestOptions,
cancelToken: cancelToken ?? _cancelToken);
return response.data;
}
/// restful patch 操作
Future patch(
String path, {
data,
Map<String, dynamic>? params,
Options? options,
CancelToken? cancelToken,
}) async {
Options requestOptions = options ?? Options();
// Map<String, dynamic> _authorization = getAuthorizationHeader();
// if (_authorization != null) {
// requestOptions = requestOptions.merge(headers: _authorization);
// }
var response = await dio.patch(path,
data: data,
queryParameters: params,
options: requestOptions,
cancelToken: cancelToken ?? _cancelToken);
return response.data;
}
/// restful delete 操作
Future delete(
String path, {
data,
Map<String, dynamic>? params,
Options? options,
CancelToken? cancelToken,
}) async {
Options requestOptions = options ?? Options();
var response = await dio.delete(path,
data: data,
queryParameters: params,
options: requestOptions,
cancelToken: cancelToken ?? _cancelToken);
return response.data;
}
/// restful post form 表单提交操作
Future postForm(
String path, {
Map<String, dynamic>? params,
Options? options,
CancelToken? cancelToken,
}) async {
Options requestOptions = options ?? Options();
var data = FormData.fromMap(params!);
var response = await dio.post(path,
data: data,
options: requestOptions,
cancelToken: cancelToken ?? _cancelToken);
return response.data;
}
}
net_cache.dart
import 'dart:collection';
import 'package:dio/dio.dart';
import 'package:pp/MyHttp/cache.dart';
import 'package:pp/MyHttp/sp.dart';
class CacheObject {
CacheObject(this.response)
: timeStamp = DateTime.now().millisecondsSinceEpoch;
Response response;
int timeStamp;
@override
bool operator ==(other) {
return response.hashCode == other.hashCode;
}
@override
int get hashCode => response.realUri.hashCode;
}
class NetCacheInterceptor extends Interceptor {
var cache = LinkedHashMap<String, CacheObject>();
@override
Future<void> onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
// TODO: implement onRequest
super.onRequest(options, handler);
if (!CACHE_ENABLE) handler.next(options);
// refresh标记是否是刷新缓存
bool refresh = options.extra["refresh"] == true;
// 是否磁盘缓存
bool cacheDisk = options.extra["cacheDisk"] == true;
// 如果刷新,先删除相关缓存
if (refresh) {
// 删除uri相同的内存缓存
delete(options.uri.toString());
// 删除磁盘缓存
if (cacheDisk) {
await SpUtil().remove(options.uri.toString());
}
handler.next(options);
}
// get 请求,开启缓存
if (options.extra["noCache"] != true &&
options.method.toLowerCase() == 'get') {
String key = options.extra["cacheKey"] ?? options.uri.toString();
// 策略 1 内存缓存优先,2 然后才是磁盘缓存
// 1 内存缓存
var ob = cache[key];
if (ob != null) {
//若缓存未过期,则返回缓存内容
if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 <
CACHE_MAXAGE) {
handler.resolve(cache[key]!.response);
} else {
//若已过期则删除缓存,继续向服务器请求
cache.remove(key);
}
}
// 2 磁盘缓存
if (cacheDisk) {
var cacheData = SpUtil().getJSON(key);
if (cacheData != null) {
handler.resolve(Response(
statusCode: 200,
data: cacheData,
requestOptions: options,
));
}
}
}
}
@override
Future<void> onResponse(
Response response, ResponseInterceptorHandler handler) async {
// TODO: implement onResponse
super.onResponse(response, handler);
// 如果启用缓存,将返回结果保存到缓存
if (CACHE_ENABLE) {
await _saveCache(response);
}
}
@override
void onError(DioError err, ErrorInterceptorHandler handler) {
// TODO: implement onError
super.onError(err, handler);
}
void delete(String key) {
cache.remove(key);
}
Future<void> _saveCache(Response object) async {
RequestOptions options = object.requestOptions;
// 只缓存 get 的请求
if (options.extra["noCache"] != true &&
options.method.toLowerCase() == "get") {
// 策略:内存、磁盘都写缓存
// 缓存key
String key = options.extra["cacheKey"] ?? options.uri.toString();
// 磁盘缓存
if (options.extra["cacheDisk"] == true) {
await SpUtil().setJSON(key, object.data);
}
// 内存缓存
// 如果缓存数量超过最大数量限制,则先移除最早的一条记录
if (cache.length == CACHE_MAXCOUNT) {
cache.remove(cache[cache.keys.first]);
}
cache[key] = CacheObject(object);
}
}
}
proxy.dart
// 是否启用代理
const PROXY_ENABLE = false;
/// 代理服务IP
const PROXY_IP = '192.168.2.237';
/// 代理服务端口
const PROXY_PORT = 8888;
request_interceptor.dart
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'app_exceptions.dart';
/// 请求处理拦截器
class RequestInterceptor extends Interceptor {
onRequest(options, handle) {
debugPrint(
'======================\n*** Request *** \nData:\n ${options.data.toString()} \nQuery:\n ${options.queryParameters.toString()} \n======================');
// // 设置cookie
// var cookie = SpUtil.getStringList('cookie');登录时保存cookie
// if (options.path != 'api/auth/login' &&
// cookie != null &&
// cookie.length > 0) {
// options.headers['cookie'] = cookie;//这里就是除了登录的情况其他都加cookie
// }
// options.headers['User-Agent'] = 'gzzoc-1';//
//
// if (options.data?.runtimeType == FormData) {
// options.headers['content-type'] = 'multipart/form-data';//FormData这种情况改content-type
// }
// // 加载动画----这个就是请求页面时的那个loading窗 --处理逻辑 我是用options?.data['showLoading']或options?.queryParameters['showLoading'],
//就是我们在传参的时候多加一个参数,这个因为前人就这样做的,也跟后端约定的,后端见showLoading不做处理。这样不是很好,反正options是有其他字段加的
// if (options?.data?.runtimeType == FormData) {
// Alert.showLoading();
// } else if ((options?.data != null &&
// false == options?.data['showLoading']) ||
// (options?.queryParameters != null &&
// false == options?.queryParameters['showLoading'])) {
// // 不显示加载动画
// } else {
// Alert.showLoading();
// }
///在这做请求时显不显示Loading的处理
handle.next(options);
//return super.onRequest(options);
}
@override
onResponse(response, handle) {
debugPrint(
'======================\n*** Response *** \n${response.toString()}');
if (response.data != null &&
response.data is Map &&
response.data['code'] == '0') {// 这个条件也是根据自己情况加的
/// Alert.hide();请求成功后关闭loading窗
// 登录请求
if (response.requestOptions.path == 'api/auth/login') {
// 缓存cookie
var cookie = response.headers['set-cookie'];
// SpUtil.putStringList('cookie', cookie!);缓存cookie
}
handle.next(response);
// return super.onResponse(response);
} else if (response.requestOptions.path ==
'api/auth/login' && // 登录登录成功, 但没有默认就诊人// 缓存cookie以便后续创建默认就诊人(需求)
response.data != null &&
response.data is Map &&
response.data['code'] == '11') {
// 缓存cookie
var cookie = response.headers['set-cookie'];
// SpUtil.putStringList('cookie', cookie!);
// Alert.hide();
handle.next(response);
} else {
handle.reject(DioError(
requestOptions: response.requestOptions,
error: response.data != null &&
response.data is Map &&
response.data['msg'] != null &&
response.data['msg'].length > 0
? response.data['msg']
: '未知错误',
response: response));
}
}
@override
onError(err, handle) {
// Alert.hide();关闭弹窗
// 账户登录异常
if (err.response != null &&
err.response?.data != null &&
err.response?.data is Map &&
err.response?.data != null &&
err.response?.data['code'] == '2') {
// Alert.showAlert(
// message: err.message ?? '未知错误',
// showCancel: false,
// onConfirm: () {
// // 清除账号缓存
// SpUtil.putString("account_phone", '');
// SpUtil.putString("account_password", '');
// SpUtil.putObject("account", '');
//
// // 退出到登录页面
// // push(Routes.login, replace: true, clearStack: true);
// },
// );
} else {
// Alert.showAlert(message: err.message ?? '未知错误', showCancel: false);//在页面显示一个错误弹窗
}
AppException appException = AppException.create(err);
err.error = appException;
return super.onError(err, handle);
}
}
sp.dart
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
/// 本地存储
class SpUtil {
static SpUtil _instance = new SpUtil._();
factory SpUtil() => _instance;
static SharedPreferences? _prefs;
SpUtil._();
static Future<void> init() async {
if (_prefs == null) {
_prefs = await SharedPreferences.getInstance();
}
}
Future<bool> setJSON(String key, dynamic jsonVal) {
String jsonString = jsonEncode(jsonVal);
return _prefs!.setString(key, jsonString);
}
dynamic getJSON(String key) {
String? jsonString = _prefs?.getString(key);
return jsonDecode(jsonString!);
}
Future<bool> setBool(String key, bool val) {
return _prefs!.setBool(key, val);
}
bool? getBool(String key) {
bool? val = _prefs?.getBool(key);
return val;
}
Future<bool> remove(String key) {
return _prefs!.remove(key);
}
}
下面就是使用了:
首先先建个model(为了方便哪种)我一般用https://javiercbk.github.io/json_to_dart/建滴。
get_science_article_entity.dart
class GetScienceArticleEntity {
String? code;
String? msg;
Data? data;
GetScienceArticleEntity({this.code, this.msg, this.data});
GetScienceArticleEntity.fromJson(Map<String, dynamic> json) {
code = json['code'];
msg = json['msg'];
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['code'] = this.code;
data['msg'] = this.msg;
if (this.data != null) {
data['data'] = this.data?.toJson();
}
return data;
}
}
class Data {
int? pageCount;
int? pageTotal;
int? pageSize;
List<PageList>? pageList;
int? pageNum;
Data(
{this.pageCount,
this.pageTotal,
this.pageSize,
this.pageList,
this.pageNum});
Data.fromJson(Map<String, dynamic> json) {
pageCount = json['pageCount'];
pageTotal = json['pageTotal'];
pageSize = json['pageSize'];
if (json['pageList'] != null) {
pageList = <PageList>[];
json['pageList'].forEach((v) {
pageList?.add(new PageList.fromJson(v));
});
}
pageNum = json['pageNum'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['pageCount'] = this.pageCount;
data['pageTotal'] = this.pageTotal;
data['pageSize'] = this.pageSize;
if (this.pageList != null) {
data['pageList'] = this.pageList?.map((v) => v.toJson()).toList();
}
data['pageNum'] = this.pageNum;
return data;
}
}
class PageList {
int? articleId;
String? articleTitle;
int? articleType;
String? articleContent;
int? articleContentType;
int? articleStatus;
String? articleCreateTime;
String? articleUpdateTime;
int? articleSort;
PageList(
{this.articleId,
this.articleTitle,
this.articleType,
this.articleContent,
this.articleContentType,
this.articleStatus,
this.articleCreateTime,
this.articleUpdateTime,
this.articleSort});
PageList.fromJson(Map<String, dynamic> json) {
articleId = json['articleId'];
articleTitle = json['articleTitle'];
articleType = json['articleType'];
articleContent = json['articleContent'];
articleContentType = json['articleContentType'];
articleStatus = json['articleStatus'];
articleCreateTime = json['articleCreateTime'];
articleUpdateTime = json['articleUpdateTime'];
articleSort = json['articleSort'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['articleId'] = this.articleId;
data['articleTitle'] = this.articleTitle;
data['articleType'] = this.articleType;
data['articleContent'] = this.articleContent;
data['articleContentType'] = this.articleContentType;
data['articleStatus'] = this.articleStatus;
data['articleCreateTime'] = this.articleCreateTime;
data['articleUpdateTime'] = this.articleUpdateTime;
data['articleSort'] = this.articleSort;
return data;
}
}
创一个类(为了方便而已)
testApi.dart
import 'package:dio/dio.dart';
import 'package:pp/MyHttp/api_response.dart';
import 'package:pp/MyHttp/http.dart';
import 'package:pp/get_science_article_entity.dart';
class TestApi {
static String _article = 'api/article/getScienceArticle';
static Future<ApiResponse<GetScienceArticleEntity>> getScienceArticle(
{int? pageNum}) async {
try {
final response =
await Https.instance.get(_article, params: {"pageNum": pageNum});
var data = GetScienceArticleEntity.fromJson(response);
return ApiResponse.completed(data);
} on DioError catch (e) {
return ApiResponse.error(e.error);
}
}
}
使用:
void _do() async {
ApiResponse<GetScienceArticleEntity> res =
await TestApi.getScienceArticle();
if (res.status != Status.COMPLETED) return;
print(res.data?.data?.pageCount);
}
结果: