From 047defebd1ccad0278941e77bbc48160887951d7 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 21 Aug 2024 15:39:29 +0800 Subject: [PATCH] :goal_net: Better request failed exceptions --- lib/exceptions/request.dart | 10 ++++++++ lib/exts.dart | 32 ++++++++++++++++++++++++ lib/providers/account_status.dart | 5 ++-- lib/providers/auth.dart | 7 +++--- lib/providers/call.dart | 5 ++-- lib/providers/content/attachment.dart | 21 ++++++++-------- lib/providers/content/channel.dart | 35 ++++++++++++++------------- lib/providers/content/posts.dart | 15 ++++++------ lib/providers/content/realm.dart | 5 ++-- lib/providers/message/adaptor.dart | 33 +++++++++++++------------ lib/providers/relation.dart | 7 +++--- lib/providers/websocket.dart | 3 ++- lib/screens/chat.dart | 6 +++-- lib/translations/zh_cn.dart | 5 ++++ 14 files changed, 124 insertions(+), 65 deletions(-) create mode 100644 lib/exceptions/request.dart diff --git a/lib/exceptions/request.dart b/lib/exceptions/request.dart new file mode 100644 index 0000000..7d1bbd3 --- /dev/null +++ b/lib/exceptions/request.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; + +class RequestException implements Exception { + final Response data; + + const RequestException(this.data); + + @override + String toString() => 'Request failed ${data.statusCode}: ${data.bodyString}'; +} diff --git a/lib/exts.dart b/lib/exts.dart index c33a01b..27abdb6 100644 --- a/lib/exts.dart +++ b/lib/exts.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; extension SolianExtenions on BuildContext { @@ -53,6 +54,37 @@ extension SolianExtenions on BuildContext { if (exception is UnauthorizedException) { content = Text('errorHappenedUnauthorized'.tr); } + if (exception is RequestException) { + String overall; + switch (exception.data.statusCode) { + case 400: + overall = 'errorHappenedRequestBad'.tr; + break; + case 401: + overall = 'errorHappenedUnauthorized'.tr; + break; + case 403: + overall = 'errorHappenedRequestForbidden'.tr; + break; + case 404: + overall = 'errorHappenedRequestNotFound'.tr; + break; + case null: + overall = 'errorHappenedRequestConnection'.tr; + break; + default: + overall = 'errorHappenedRequestUnknown'.tr; + break; + } + + if (exception.data.statusCode != null) { + content = Text( + '$overall\n\n(${exception.data.statusCode}) ${exception.data.bodyString}', + ); + } else { + content = Text(overall); + } + } return showDialog( useRootNavigator: true, diff --git a/lib/providers/account_status.dart b/lib/providers/account_status.dart index 27fab17..e7332b1 100644 --- a/lib/providers/account_status.dart +++ b/lib/providers/account_status.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/models/account_status.dart'; import 'package:solian/providers/auth.dart'; @@ -74,7 +75,7 @@ class StatusProvider extends GetConnect { } if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -88,7 +89,7 @@ class StatusProvider extends GetConnect { final resp = await client.delete('/users/me/status'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; diff --git a/lib/providers/auth.dart b/lib/providers/auth.dart index 1f96766..e1541de 100644 --- a/lib/providers/auth.dart +++ b/lib/providers/auth.dart @@ -7,6 +7,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:get/get.dart'; import 'package:get/get_connect/http/src/request/request.dart'; import 'package:solian/controllers/chat_events_controller.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/providers/websocket.dart'; import 'package:solian/services.dart'; @@ -82,7 +83,7 @@ class AuthProvider extends GetConnect { 'grant_type': 'refresh_token', }); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } credentials = TokenSet( accessToken: resp.body['access_token'], @@ -159,7 +160,7 @@ class AuthProvider extends GetConnect { 'password': password, }); if (resp.statusCode != 200) { - throw Exception(resp.body); + throw RequestException(resp); } else if (resp.body['is_finished'] == false) { throw RiskyAuthenticateException(resp.body['ticket']['id']); } @@ -219,7 +220,7 @@ class AuthProvider extends GetConnect { final client = configureClient('auth'); final resp = await client.get('/users/me'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } userProfile.value = resp.body; diff --git a/lib/providers/call.dart b/lib/providers/call.dart index 80c436e..298d9db 100644 --- a/lib/providers/call.dart +++ b/lib/providers/call.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; import 'package:livekit_client/livekit_client.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -89,7 +90,7 @@ class ChatCallProvider extends GetxController { Future<(String, String)> getRoomToken() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -102,7 +103,7 @@ class ChatCallProvider extends GetxController { endpoint = 'wss://${resp.body['endpoint']}'; return (token!, endpoint!); } else { - throw Exception(resp.bodyString); + throw RequestException(resp); } } diff --git a/lib/providers/content/attachment.dart b/lib/providers/content/attachment.dart index 45b8bdd..765a502 100644 --- a/lib/providers/content/attachment.dart +++ b/lib/providers/content/attachment.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; import 'package:path/path.dart'; import 'package:solian/models/attachment.dart'; @@ -90,7 +91,7 @@ class AttachmentProvider extends GetConnect { Map? metadata, ) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient( 'uc', @@ -119,7 +120,7 @@ class AttachmentProvider extends GetConnect { }); final resp = await client.post('/attachments', payload); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return Attachment.fromJson(resp.body); @@ -132,7 +133,7 @@ class AttachmentProvider extends GetConnect { Map? metadata, ) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('uc'); @@ -157,7 +158,7 @@ class AttachmentProvider extends GetConnect { 'metadata': metadata, }); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return AttachmentPlaceholder.fromJson(resp.body); @@ -170,7 +171,7 @@ class AttachmentProvider extends GetConnect { String cid, ) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient( 'uc', @@ -182,7 +183,7 @@ class AttachmentProvider extends GetConnect { }); final resp = await client.post('/attachments/multipart/$rid/$cid', payload); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return Attachment.fromJson(resp.body); @@ -194,7 +195,7 @@ class AttachmentProvider extends GetConnect { bool isMature = false, }) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('files'); @@ -204,7 +205,7 @@ class AttachmentProvider extends GetConnect { }); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -212,13 +213,13 @@ class AttachmentProvider extends GetConnect { Future deleteAttachment(int id) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('files'); var resp = await client.delete('/attachments/$id'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; diff --git a/lib/providers/content/channel.dart b/lib/providers/content/channel.dart index 3dbf0a7..22eaef4 100644 --- a/lib/providers/content/channel.dart +++ b/lib/providers/content/channel.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/models/channel.dart'; import 'package:solian/providers/auth.dart'; @@ -17,7 +18,7 @@ class ChannelProvider extends GetxController { Future refreshAvailableChannel() async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); isLoading.value = true; final resp = await listAvailableChannel(); @@ -30,13 +31,13 @@ class ChannelProvider extends GetxController { Future getChannel(String alias, {String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); final resp = await client.get('/channels/$realm/$alias'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -45,13 +46,13 @@ class ChannelProvider extends GetxController { Future getMyChannelProfile(String alias, {String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); final resp = await client.get('/channels/$realm/$alias/me'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -60,7 +61,7 @@ class ChannelProvider extends GetxController { Future getChannelOngoingCall(String alias, {String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); @@ -68,7 +69,7 @@ class ChannelProvider extends GetxController { if (resp.statusCode == 404) { return null; } else if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -76,13 +77,13 @@ class ChannelProvider extends GetxController { Future listChannel({String scope = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); final resp = await client.get('/channels/$scope'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -90,13 +91,13 @@ class ChannelProvider extends GetxController { Future listAvailableChannel({String realm = 'global'}) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); final resp = await client.get('/channels/$realm/me/available'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -104,13 +105,13 @@ class ChannelProvider extends GetxController { Future createChannel(String scope, dynamic payload) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); final resp = await client.post('/channels/$scope', payload); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -119,7 +120,7 @@ class ChannelProvider extends GetxController { Future createDirectChannel( BuildContext context, String scope) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final related = await showModalBottomSheet( useRootNavigator: true, @@ -142,7 +143,7 @@ class ChannelProvider extends GetxController { 'is_encrypted': false, }); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -150,13 +151,13 @@ class ChannelProvider extends GetxController { Future updateChannel(String scope, int id, dynamic payload) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final client = auth.configureClient('messaging'); final resp = await client.put('/channels/$scope/$id', payload); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; diff --git a/lib/providers/content/posts.dart b/lib/providers/content/posts.dart index 332de98..11e5943 100644 --- a/lib/providers/content/posts.dart +++ b/lib/providers/content/posts.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/services.dart'; @@ -29,7 +30,7 @@ class PostProvider extends GetConnect { : '/recommendations/$channel?${queries.join('&')}', ); if (resp.statusCode != 200) { - throw Exception(resp.body); + throw RequestException(resp); } return resp; @@ -37,7 +38,7 @@ class PostProvider extends GetConnect { Future listDraft(int page) async { final AuthProvider auth = Get.find(); - if (auth.isAuthorized.isFalse) throw UnauthorizedException(); + if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); final queries = [ 'take=${10}', @@ -46,7 +47,7 @@ class PostProvider extends GetConnect { final client = auth.configureClient('interactive'); final resp = await client.get('/posts/drafts?${queries.join('&')}'); if (resp.statusCode != 200) { - throw Exception(resp.body); + throw RequestException(resp); } return resp; @@ -64,7 +65,7 @@ class PostProvider extends GetConnect { ]; final resp = await get('/posts?${queries.join('&')}'); if (resp.statusCode != 200) { - throw Exception(resp.body); + throw RequestException(resp); } return resp; @@ -73,7 +74,7 @@ class PostProvider extends GetConnect { Future listPostReplies(String alias, int page) async { final resp = await get('/posts/$alias/replies?take=${10}&offset=$page'); if (resp.statusCode != 200) { - throw Exception(resp.body); + throw RequestException(resp); } return resp; @@ -82,7 +83,7 @@ class PostProvider extends GetConnect { Future getPost(String alias) async { final resp = await get('/posts/$alias'); if (resp.statusCode != 200) { - throw Exception(resp.body); + throw RequestException(resp); } return resp; @@ -91,7 +92,7 @@ class PostProvider extends GetConnect { Future getArticle(String alias) async { final resp = await get('/articles/$alias'); if (resp.statusCode != 200) { - throw Exception(resp.body); + throw RequestException(resp); } return resp; diff --git a/lib/providers/content/realm.dart b/lib/providers/content/realm.dart index f63bf21..5353947 100644 --- a/lib/providers/content/realm.dart +++ b/lib/providers/content/realm.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/models/realm.dart'; import 'package:solian/providers/auth.dart'; @@ -28,7 +29,7 @@ class RealmProvider extends GetxController { final resp = await client.get('/realms/$alias'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -42,7 +43,7 @@ class RealmProvider extends GetxController { final resp = await client.get('/realms/me/available'); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; diff --git a/lib/providers/message/adaptor.dart b/lib/providers/message/adaptor.dart index daeeec2..15615ba 100644 --- a/lib/providers/message/adaptor.dart +++ b/lib/providers/message/adaptor.dart @@ -1,5 +1,6 @@ import 'package:floor/floor.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/models/channel.dart'; import 'package:solian/models/event.dart'; import 'package:solian/models/pagination.dart'; @@ -29,20 +30,20 @@ Future getRemoteEvent(int id, Channel channel, String scope) async { if (resp.statusCode == 404) { return null; } else if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return Event.fromJson(resp.body); } Future<(List, int)?> getRemoteEvents( - Channel channel, - String scope, { - required int remainDepth, - bool Function(List items)? onBrake, - take = 10, - offset = 0, - }) async { + Channel channel, + String scope, { + required int remainDepth, + bool Function(List items)? onBrake, + take = 10, + offset = 0, +}) async { if (remainDepth <= 0) { return null; } @@ -57,7 +58,7 @@ Future<(List, int)?> getRemoteEvents( ); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } final PaginationResult response = PaginationResult.fromJson(resp.body); @@ -69,13 +70,13 @@ Future<(List, int)?> getRemoteEvents( } final expandResult = (await getRemoteEvents( - channel, - scope, - remainDepth: remainDepth - 1, - take: take, - offset: offset + result.length, - )) - ?.$1 ?? + channel, + scope, + remainDepth: remainDepth - 1, + take: take, + offset: offset + result.length, + )) + ?.$1 ?? List.empty(); return ([...result, ...expandResult], response.count); diff --git a/lib/providers/relation.dart b/lib/providers/relation.dart index d58f2fa..9e07f1f 100644 --- a/lib/providers/relation.dart +++ b/lib/providers/relation.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/models/account.dart'; import 'package:solian/models/relations.dart'; import 'package:solian/providers/auth.dart'; @@ -42,7 +43,7 @@ class RelationshipProvider extends GetxController { final client = auth.configureClient('auth'); final resp = await client.post('/users/me/relations?related=$username', {}); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -57,7 +58,7 @@ class RelationshipProvider extends GetxController { {}, ); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; @@ -71,7 +72,7 @@ class RelationshipProvider extends GetxController { {'status': status}, ); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } return resp; diff --git a/lib/providers/websocket.dart b/lib/providers/websocket.dart index abcc4c8..e460488 100644 --- a/lib/providers/websocket.dart +++ b/lib/providers/websocket.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:get/get.dart'; +import 'package:solian/exceptions/request.dart'; import 'package:solian/models/notification.dart'; import 'package:solian/models/packet.dart'; import 'package:solian/models/pagination.dart'; @@ -148,7 +149,7 @@ class WebSocketProvider extends GetxController { 'device_id': deviceUuid, }); if (resp.statusCode != 200) { - throw Exception(resp.bodyString); + throw RequestException(resp); } } diff --git a/lib/screens/chat.dart b/lib/screens/chat.dart index 4753dca..7a9b6c8 100644 --- a/lib/screens/chat.dart +++ b/lib/screens/chat.dart @@ -80,13 +80,15 @@ class _ChatScreenState extends State { contentPadding: const EdgeInsets.symmetric(horizontal: 8), ), onTap: () { - final ChannelProvider provider = Get.find(); - provider + final ChannelProvider channels = Get.find(); + channels .createDirectChannel(context, 'global') .then((resp) { if (resp != null) { _channels.refreshAvailableChannel(); } + }).catchError((e) { + context.showErrorDialog(e); }); }, ), diff --git a/lib/translations/zh_cn.dart b/lib/translations/zh_cn.dart index b471204..1d815cd 100644 --- a/lib/translations/zh_cn.dart +++ b/lib/translations/zh_cn.dart @@ -42,6 +42,11 @@ const i18nSimplifiedChinese = { 'notification': '通知', 'errorHappened': '发生错误了', 'errorHappenedUnauthorized': '未经授权的请求,请登录或尝试重新登录。', + 'errorHappenedRequestBad': '请求错误,服务器拒绝处理该请求,请检查您的请求数据。', + 'errorHappenedRequestForbidden': '请求错误,权限不足。', + 'errorHappenedRequestNotFound': '请求错误,请求的数据不存在。', + 'errorHappenedRequestConnection': '网络请求失败,请检查连接状态与服务状态后再试。', + 'errorHappenedRequestUnknown': '请求错误,类型未知,请将本提示完整截图提交反馈。', 'forgotPassword': '忘记密码', 'email': '邮件地址', 'username': '用户名',