При разработке приложения Flutter вы, вероятно, хотите свести затраты на разработку к минимуму, сделать решение быстрым, надежным и безопасным. В этом руководстве (точнее, техническом обзоре с комментариями) я объясню, как подключить ваше приложение Flutter к серверной части с помощью HTTP REST API и gRPC. Вы узнаете, как правильно выбрать серверную часть для своего приложения, как ее настроить и как подключить к ней ваше приложение.
Что выбрать?
Не секрет, что REST API является наиболее распространенным способом связи между интерфейсом и сервером в современных веб-приложениях и инфраструктурах на основе микросервисов. Однако стоит отметить, что микросервисы могут использовать и другие способы связи.
HTTP REST API использует протокол HTTP для передачи данных между клиентом и сервером. Он прост в использовании и понятен большинству разработчиков. Чтобы использовать его, вам нужно определить запросы, которые ваше приложение должно отправлять на сервер, а также структуру ответов, которые вы ожидаете получить. В этой статье приведен пример использования пакета Dio.
gRPC (вызов удаленных процедур Google) — это новый подход к обмену данными между клиентом и сервером, основанный на архитектуре RPC с открытым исходным кодом. Он использует формат обмена сообщениями Protobuf, который является высокоэффективным форматом для обмена сообщениями с высокой степенью упаковки (благодаря принудительному использованию возможностей http/2) для сериализации структурированных данных. В некоторых случаях использование gRPC API может быть более эффективным, чем REST API.
При разработке приложения фронтенд и бэкенд обычно создают разные люди с разной компетенцией. Такие инструменты, как swagger и redoc, используются для предоставления инструкций по использованию REST. В случае с gRPC фронтенд получает практически готовый к реализации код. Также стоит учитывать тот факт, что если в проекте задействовано веб-приложение, использование gRPC для мобильного приложения может оказаться слишком дорогим.
Соединение с HTTP REST
Для работы с REST API я рекомендую использовать пакет Dio, поскольку он более функционален и удобен, чем стандартный http-пакет. Если вы создали веб-приложения, это похоже на axios.
Для использования Dio вам необходимо создать класс для работы с сетевыми подключениями, в котором вы будете определять запросы, которые ваше приложение должно отправлять на сервер, а также структуру ответов, которые вы ожидаете получить.
"флаттер паб добавить дио"
Добавьте пакет dio в свой проект
Создадим класс для работы с сетевыми подключениями:
import 'package:dio/dio.dart'; class NetworkService { late final Dio _dio; final JsonEncoder _encoder = const JsonEncoder(); static final NetworkService _instance = NetworkService.internal(); NetworkService.internal(); static NetworkService get instance => _instance; Future<void> initClient() async { _dio = Dio( BaseOptions( baseUrl: Constant.baseUrl, connectTimeout: 60000, receiveTimeout: 6000, ), ); // A place for interceptors. For example, for authentication and logging } Future<dynamic> get( String url, { Map<String, dynamic>? queryParameters, }) async { try { final response = await _dio.get(url, queryParameters: queryParameters); return response.data; } on DioError catch (e) { final data = Map<String, dynamic>.from(e.response?.data); throw Exception(data['message'] ?? "Error while fetching data"); } catch (e) { rethrow; } } Future<dynamic> download(String url, String path) async { return _dio.download(url, path).then((Response response) { if (response.statusCode! < 200 || response.statusCode! > 400) { throw Exception("Error while fetching data"); } return response.data; }).onError((error, stackTrace) { throw Exception(error); }); } Future<dynamic> delete(String url) async { return _dio.delete(url).then((Response response) { if (response.statusCode! < 200 || response.statusCode! > 400) { throw Exception("Error while fetching data"); } return response.data; }).onError((DioError error, stackTrace) { _log(error.response); }); } Future<dynamic> post(String url, {body, encoding}) async { try { final response = await _dio.post(url, data: _encoder.convert(body)); return response.data; } on DioError catch (e) { throw Exception(e.response?.data['detail'] ?? e.toString()); } catch (e) { rethrow; } } Future<dynamic> postFormData(String url, {required FormData data}) async { try { final response = await _dio.post(url, data: data); return response.data; } on DioError catch (e) { throw Exception(e.response?.data['detail'] ?? e.toString()); } catch (e) { rethrow; } } Future<dynamic> patch(String url, {body, encoding}) async { try { final response = await _dio.patch(url, data: _encoder.convert(body)); return response.data; } on DioError catch (e) { throw Exception(e.response?.data['detail'] ?? e.toString()); } catch (e) { rethrow; } } Future<dynamic> put(String url, {body, encoding}) async { try { final response = await _dio.put(url, data: _encoder.convert(body)); return response.data; } on DioError catch (e) { throw e.toString(); } catch (e) { rethrow; } } }
Пример
final NetworkService _client; Future<String> login(LoginRequest loginRequest) async { try { final jsonData = await _client.post( "${Constant.baseUrl}/v1/auth/login", body: loginRequest.toJson() ); return jsonData['access_token'] } catch (e) { rethrow; } }
Соединение с gRPC
Для работы с gRPC необходимо использовать сгенерированный код на основе протофайлов. Создайте сервис для работы, который будет использовать класс HelloClient, сгенерированный с помощью gRPC. Когда вы создаете экземпляр HelloClient, вам нужно будет передать ему канал, который будет использоваться для отправки запросов.
Теперь интегрируйте сгенерированный клиент gRPC в ваше приложение Flutter:
"флаттер паб добавить grpc"
Добавьте пакет grpc в свой проект
Создание сервиса для работы:
import 'package:grpc/grpc.dart'; //import your autogenerate code import '../services/proto/hello.pbgrpc.dart'; class HelloService { ///here enter your host without the http part String baseUrl = "example.com"; HelloService._internal(); static final HelloService _instance = HelloService._internal(); factory HelloService() => _instance; ///static HelloService instance that we will call when we want to make requests static HelloService get instance => _instance; ///HelloClient is the class that was generated for us when we ran the generation command ///We will pass a channel to it to intialize it late HelloClient _helloClient; ///this will be used to create a channel once we create this class. ///Call HelloService().init() before making any call. Future<void> init() async { final channel = ClientChannel( baseUrl, port: 443, options: const ChannelOptions(), ); _helloClient = HelloClient(channel); } ///provide public access to the HelloClient instance HelloClient get helloClient { return _helloClient; } }
В вашей основной функции инициализируйте класс HelloService, как показано ниже.
HelloService().init();
Сделать вызов gRPC
Future<void> sayHello() async { try { HelloRequest helloRequest = HelloRequest(); helloRequest.name = "Itachi"; var res = await HelloService.instance.helloClient.sayHello(helloRequest); } catch (e) { print(e); } }
Сравнение REST и gRPC для приложений Flutter
- Преимущества REST: простой, понятный, широко поддерживаемый, кэшируемый и подходящий для большинства приложений.
- Недостатки REST: более высокая задержка, менее эффективная сериализация, ограниченные возможности потоковой передачи.
- Преимущества gRPC: низкая задержка, эффективная сериализация, поддержка потоковой передачи, мощные клиентские библиотеки и идеальное решение для приложений и микросервисов, работающих в реальном времени.
- Недостатки gRPC: более крутая кривая обучения, не такая удобочитаемая для человека, меньшая поддержка в веб-браузерах и может быть излишним для простых приложений.
Выбирая между REST и gRPC для своего приложения Flutter, учитывайте конкретные потребности и требования вашего приложения. REST — хороший выбор для большинства приложений, в то время как gRPC хорошо подходит для приложений реального времени или приложений со сложными шаблонами связи между службами.
В этой статье мы рассмотрели, как организовать приложение Flutter с серверной частью API, сосредоточившись на двух основных подходах: HTTP REST и gRPC. Мы обсудили разработку API, реализацию клиентов во Flutter.
Поняв преимущества и недостатки каждого подхода, вы сможете принять взвешенное решение при разработке приложения Flutter с серверной частью API. Выберите наилучший подход для своего приложения с учетом его конкретных потребностей и создавайте эффективные, масштабируемые и удобные в обслуживании приложения.