携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
本文主要介绍Getx中GetContact,它可以便捷的通过http或websockets进行前后台通信。
1. 自定义配置
使用GetContact通常可以用于网络请求或者通信,这里以网络请求为例
先看下GetContact组成,提供了请求时间,最大请求次数等。
同时对httpClient进行了封装,我们可以设置baseUrl,post等请求。
GetConnect具有多种自定义配置。你可以配置base Url,配置响应,配置请求,添加权限验证,甚至是尝试认证的次数,除此之外,还可以定义一个标准的解码器,该解码器将把您的所有请求转换为您的模型,而不需要任何额外的配置。
我们创建一个基础的网络请求继承于GetConnect
class Net extends GetConnect {
Net() : super(timeout: const Duration(seconds: 30), userAgent: 'Sesame-Client') {
httpClient.addRequestModifier<Object?>((request) async {
_logRequest(request);
await _setupHeader(request);
return request;
});
}
@override
String get baseUrl => currentEnvironment.host;
@override
Decoder<NetResponse> get defaultDecoder => (data) {
log("---- 响应 ----\n$data");
try {
return NetResponse.fromJson(data);
} catch (error) {
return NetResponse(NetCode.serverError, null)..message = '服务端未知错误';
}
};
String get _platform {
if (Platform.isIOS) {
return 'iOS';
} else if (Platform.isAndroid) {
return 'android';
} else {
return 'web';
}
}
Future _setupHeader(Request request) async {
request.headers['Sesame-Platform'] = _platform;
final token = await StoreToken.getToken();
if (token != null) request.headers['Authorization'] = token;
}
void _logRequest(Request request) async {
var str = "---- 请求 ----\nmethod: ${request.method}\nurl: ${request.url}\nquery: ${request.url.queryParameters}";
if (request.method != 'post' || request.headers['content-type'] != 'application/json') {
log(str);
return;
}
const decoder = Utf8Decoder();
List<List<int>> bodyBytes = [];
request.bodyBytes.asBroadcastStream(onListen: (subscribe) {
subscribe.onData((data) => bodyBytes.add(data));
subscribe.onDone(() {
str += '\nbody: ${(bodyBytes.map((e) => decoder.convert(e)).join())}';
log(str);
});
});
}
}
通过addRequestModifier进行请求拦截,添加一些日志打印以及请求头设置。addResponseModifier响应拦截处理我们的请求结果,这是使用的defaultDecoder进行解码,
NetResponse为我们自定义的请求体格式,可以定义一些约定好的状态code,通过catch处理我们的错误状态。
2. 网络请求
你能轻松的通过extend ,GetConnect就能使用GET/POST/PUT/DELETE/SOCKET方法与你的Rest API或websockets通信。
class UserProvider extends GetConnect {
// Get request
Future<Response> getUser(int id) => get('http://youapi/users/$id');
// Post request
Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
// Post request with File
Future<Response<CasesModel>> postCases(List<int> image) {
final form = FormData({
'file': MultipartFile(image, filename: 'avatar.png'),
'otherFile': MultipartFile(image, filename: 'cover.png'),
});
return post('http://youapi/users/upload', form);
}
GetSocket userMessages() {
return socket('https://yourapi/users/socket');
}
}
- 自定义
bool get shouldRequest => true;
Future? request<T>({required ValueGetter<Future<T>> api, ValueSetter<T>? success, ValueSetter<Error>? fail}) async {
if (!shouldRequest) {
EasyLoading.showToast('请完善信息后重试');
return;
}
EasyLoading.show();
return api().then((value) {
EasyLoading.dismiss();
if (success != null) success(value);
}).catchError((error) {
EasyLoading.dismiss();
EasyLoading.showToast(error.toString());
if (fail != null) fail(error);
if (!const bool.fromEnvironment("dart.vm.product")) throw error;
});
}
Future<T> get<T>(String uri, Decoder<T> decoder, {Map<String, dynamic>? query}) async {
final res = (await net.get<NetResponse>(uri, query: query, decoder: net.defaultDecoder)).body;
return _parse(res, decoder);
}
Future<T> post<T>(String uri, Map<String, dynamic> body, Decoder<T> decoder) async {
print(body);
final res =
(await net.post<NetResponse>(uri, body, contentType: 'application/json', decoder: net.defaultDecoder)).body;
return _parse(res, decoder);
}
Future<T> patch<T>(String uri, Map<String, dynamic> body, Decoder<T> decoder) async {
final res =
(await net.patch<NetResponse>(uri, body, contentType: 'application/json', decoder: net.defaultDecoder)).body;
return _parse(res, decoder);
}
Future<T> delete<T>(String uri, Map<String, dynamic>? query, Decoder<T> decoder) async {
final res = (await net.delete<NetResponse>(uri, query: query, decoder: net.defaultDecoder)).body;
return _parse(res, decoder);
}
使用
void requestData() {
get('home/chat/list', (data) => (data as List<dynamic>).map((e) => ChatModel.fromJson(e)).toList()).then((value) => {
listData.value = value
});
}
3. Getx+Dio
我们也可以使用Dio进行网络请求,这里简单封装下
class HttpManager {
static final HttpManager _instance = HttpManager._internal();
factory HttpManager() => _instance;
late Dio dio;
HttpManager._internal() {
// BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
BaseOptions options = BaseOptions(
// 请求基地址,可以包含子路径
baseUrl: currentEnvironment.host,
// baseUrl: storage.read(key: STORAGE_KEY_APIURL) ?? SERVICE_API_BASEURL,
//连接服务器超时时间,单位是毫秒.
connectTimeout: 10000,
// 响应流上前后两次接受到数据的间隔,单位为毫秒。
receiveTimeout: 5000,
// Http请求头.
headers: {},
contentType: 'application/json; charset=utf-8',
responseType: ResponseType.json,
);
dio = Dio(options);
}
/// restful get 操作
Future get(
String path, {
dynamic? queryParameters,
Options? options,
}) async {
var response = await dio.get(
path,
queryParameters: queryParameters,
options: options,
);
print(response.data);
return response.data;
}
}
使用
RxList listData = [].obs;
Future<void> requestData() async {
var response = await HttpManager().get('home/chat/list');
List<dynamic> data = response['data'];
listData.value = data.map((e) => ChatModel.fromJson(e)).toList();
}
打印结果:
4. 小结
Getx对我们的http请求做了封装,我们可以自定义其配置项,里面监听和拦截方法方便我们放些请求头设置或者自定义的操作,定义一个标准的解码器,该解码器将把您的所有请求转换为需要的模型,而不需要任何额外的配置。之后复值配合我们的Rx<T> 刷新页面。我们也可以使用Dio封装进行网络请求之后附值,刷新页面,响应式表达。