我对图像上传场景相当陌生,社区能否建议将图像文件上传到服务器时遵循的最佳实践。端点已经开发出来,现在我正在使用 dio 和改造直接调用 api。我想了解基于行业标准的最佳实践,其中包括错误处理和处理任何其他场景,例如重新上传等..
如果您使用 dio,您可能熟悉“拦截器”,对吗? 有很多方法可以让你的拦截器变得干净。这是我的
class AppInterceptors extends Interceptor {
const AppInterceptors(this.dio);
final Dio dio;
@override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
String? token = await TokenStorage.getToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
super.onRequest(options, handler);
}
@override
Future<void> onError(
DioException err,
ErrorInterceptorHandler handler,
) async {
ErrorResponse resp = ErrorResponse.fromJson(err.response?.data);
log(
'[${err.response?.statusCode ?? '?'}] ${err.message}',
name: 'interceptor',
);
log('Error ID: ${resp.error?.errorId}', name: 'Interceptor');
log('Error Messages:', name: 'Interceptor');
for (String msg in resp.error?.errorMessages ?? <String>[]) {
log(' - $msg', name: 'Interceptor');
}
bool unauthorized = err.response?.statusCode == 401;
String? refreshToken = await TokenStorage.getRefreshToken();
log('${err.response?.statusCode} and $unauthorized', name: 'findme');
if (unauthorized && refreshToken.notEmptyOrNull) {
AuthApi refresher = AuthApi.create(dio);
LoginResponse? refreshResult = await refresher
.refreshToken(
RefreshTokenRequest(refreshToken: refreshToken).toJson(),
)
.onError<DioException>((DioException? error, StackTrace stackTrace) {
Future<void>.microtask(() async {
log('expiredRefreshToken: $refreshToken');
await TokenStorage.deleteAllToken();
ToastHelper.error('Session expired, please login again.');
throwToAuthScreen();
});
return null;
});
if (refreshResult != null) {
await TokenStorage.writeToken(refreshResult.jwtToken ?? '');
await TokenStorage.writeRefreshToken(refreshResult.refreshToken ?? '');
// retry the previous request
RequestOptions options = err.requestOptions;
options.headers['Authorization'] = 'Bearer ${refreshResult.jwtToken}';
try {
Response<dynamic> response = await dio.fetch(options);
return handler.resolve(response);
} catch (e) {
ToastHelper.error(
'An unknown error occurred, please try again later.',
);
return;
}
}
} else {
throwToAuthScreen();
ToastHelper.error('There was a problem, Please try again.');
}
return handler.reject(err);
}
}
这是我的 http_client
class HttpClient {
static Dio init({
required String baseUrl,
int sendTimeout = 60000,
int connectTimeout = 60000,
int receiveTimeout = 60000,
String contentType = Headers.jsonContentType,
ResponseType responseType = ResponseType.plain,
}) {
Dio dio = Dio(BaseOptions(
baseUrl: baseUrl,
receiveTimeout: Duration(seconds: receiveTimeout),
connectTimeout: Duration(seconds: connectTimeout),
sendTimeout: Duration(seconds: sendTimeout),
contentType: contentType,
responseType: responseType,
));
// TODO(Andrew): add ssl pinning
// dio.interceptors.add(
// CertificatePinningInterceptor(),
// );
dio.interceptors.add(AppInterceptors(dio));
dio.interceptors.add(PrettyDioLogger(
request: kDebugMode,
requestBody: kDebugMode,
requestHeader: kDebugMode,
responseHeader: false,
responseBody: kDebugMode,
error: true,
logPrint: (Object object) => log(
object.toString(),
name: DateTime.now().toTimeShort,
),
maxWidth: 80,
));
dio.interceptors.add(CurlLoggerDioInterceptor(printOnSuccess: true));
dio.interceptors.add(RetryInterceptor(
dio: dio,
retries: 3,
retryDelays: <Duration>[3.seconds, 5.seconds, 15.seconds],
));
return dio;
}
}
RetryInterceptor 是 dio 拦截器的封装和扩展。你可以制作自己的拦截器,就像我上面写的那样(AppInterceptor)这个拦截器有3个功能:onRequest、onError、onResponse。 正如其名称所示,这 3 个函数是不言自明的。您可以拥有许多拦截器,使您能够管理应用程序的每个请求,例如将令牌添加到标头,有很多方法可以做到这一点,但在我的代码中,我将其尽可能模块化。与 Riverpod 结合,我可以使用 Riverpod 制作多个端点并模块化使用它
@riverpod
class DioClient extends _$DioClient {
@override
Dio build() {
return HttpClient.init(baseUrl: F.baseUrl);
}
}