تتمثل إحدى المهام الشائعة في معظم التطبيقات في كتابة عملاء لإرسال واسترجاع البيانات من واجهة برمجة التطبيقات. وبدلاً من كتابة هؤلاء العملاء بأنفسنا، يمكننا إنشاء عملاء واجهة برمجة التطبيقات في Flutter. في هذا المنشور، سنستعرض تنفيذًا كاملاً باستخدام واجهة برمجة تطبيقات وهمية.
- posts:لإرجاع كافة المشاركات.
- post: الذي يأخذ idمعلمة ويعيد منشورًا واحدًا.
- add: والذي يتطلب jsonنصًا وسيُحاكي إضافة منشور.
- update: الذي يأخذ idمعلمة ويتطلب jsonنصًا وسيعمل على محاكاة تحديث المنشور.
- delete: والذي يأخذ idمعلمة وسيقوم بمحاكاة حذف المنشور.
ليس من الضروري أن يكون العلم --delete-conflicting-ouputs ضروريًا، ولكنه يضمن الكتابة فوق الملفات التي تم إنشاؤها.
إنشاء عميل Chopper
بعد إنشاء ملفنا PostService، يمكننا المتابعة بإنشاء مثيل لملف ChopperClient. ChopperClientيحتوي ملف . على السمة المطلوبة baseUrlالتي سنقوم بتعيينها لعنوان URL الخاص بواجهة برمجة التطبيقات الخاصة بنا: https://jsonplaceholder.typicode.com . كما أنه يأخذ قائمة ChopperServiceبالمثيلات.
import 'package:chopper/chopper.dart';
import 'package:chopper_client_generator/post_service.dart';
void main() async {
final chopper = ChopperClient(
baseUrl: Uri.parse('https://jsonplaceholder.typicode.com'),
services: [
PostService.create(),
],
);
final postService = chopper.getService<PostService>();
try {
final response = await postService.posts();
if (response.isSuccessful) {
final body = response.body;
print(body);
} else {
throw Exception(response.error);
}
} catch (error) {
print(error);
}
}
في المثال أعلاه، قمنا بإنشاء مثيل من ChopperClient وحفظه في chopperالمتغير. داخل العميل، قمنا بتحديد عنوان URL الأساسي الخاص بنا وأضفنا PostService.
بعد ذلك، نستخدم chopperالمتغير للوصول إلى PostServiceالدالة getService. في الخدمة، يمكننا استدعاء جميع مساراتنا. يمكنك أن ترى أننا قمنا بتغليف استدعاء واجهة برمجة التطبيقات باستخدام try catch لالتقاط الاستثناءات المحتملة.
بخلاف ذلك، ستعيد مكالمة واجهة برمجة التطبيقات استجابة. في الاستجابة، لدينا إمكانية الوصول إلى isSuccessfulgetter is، وسيتحقق هذا getter مما إذا statusCodeكانت قيمة الاستجابة أكبر أو تساوي 200أو أصغر من 300.
إذا كانت الاستجابة ناجحة فسوف نقوم بطباعة النص وإلا فسوف نلقي الخطأ.
الاتصال بخدمة الحصول على المنشورات وطلبات النشر
الآن عندما نقوم بتشغيل mainوظيفتنا نحصل على النتيجة التالية من واجهة برمجة التطبيقات داخل محطتنا:
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
]
كما ترى فإن الرد الذي تلقيناه عبارة عن قائمة من كائنات JSON المنشورة.
إذا أردنا تقديم طلب مختلف، يتعين علينا فقط تغيير الوظيفة التي نستدعيها على postService.
import 'package:chopper/chopper.dart';
import 'package:chopper_client_generator/post_service.dart';
void main() async {
final chopper = ChopperClient(
baseUrl: Uri.parse('https://jsonplaceholder.typicode.com'),
services: [
PostService.create(),
],
);
final postService = chopper.getService<PostService>();
try {
final response = await postService.post(2);
if (response.isSuccessful) {
final body = response.body;
print(body);
} else {
throw Exception(response.error);
}
} catch (error) {
print(error);
}
}
في الكود أعلاه، قمنا بتغيير الدالة التي نستدعيها إلى postService. postتأخذ postالدالة idمعلمة وستعيد المنشور الذي يحتوي على المطابقة id:
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
]
بالطبع، يمكنك استدعاء عدد لا حصر له من الطلبات المختلفة على النظام postServiceكما تريد. ومع ذلك، تأكد من تضمين طلباتك دائمًا في أمر try-catch، في حالة حدوث خطأ ما في الواجهة الخلفية.
استدعاء طلبات الإضافة والتحديث والحذف
لاستدعاء addالطلبات update، نحتاج إلى إجراء بعض التغييرات الإضافية لضمان تحويل نص استجابتنا بشكل صحيح إلى JSON.
import 'package:chopper/chopper.dart';
import 'package:chopper_client_generator/post_service.dart';
void main() async {
final chopper = ChopperClient(
baseUrl: Uri.parse('https://jsonplaceholder.typicode.com'),
converter: const JsonConverter(),
errorConverter: const JsonConverter(),
services: [
PostService.create(),
],
);
final postService = chopper.getService<PostService>();
try {
final response = await postService.add({
'title': 'foo',
'body': 'bar',
'userId': 1,
});
if (response.isSuccessful) {
final body = response.body;
print(body);
} else {
throw Exception(response.error);
}
} catch (error) {
print(error);
}
}
في مقتطف التعليمات البرمجية هذا، أضفنا معلمة converterand errorConverterإلى مثيلنا ChopperClient. توفر حزمة Chopper فئة JsonConverterيمكن استخدامها لتحويل JSON. errorConverterلا تكون المعلمة ضرورية في هذه الحالة. ومع ذلك، أريد فقط أن أوضح لك أنه يمكنك أيضًا تحويل أخطائك إلى JSON.
{title: foo, body: bar, userId: 1, id: 101}
كما هو موضح من قبل، تتطلب addوظيفة الفصل PostServiceنصًا ونوفر نص JSON https://jsonplaceholder.typicode.com/guide/ الذي تتوقعه واجهة برمجة التطبيقات JSONPlaceholder الخاصة بنا . عندما نقوم بتشغيل الكود الخاص بنا، سترى أننا نتلقى الاستجابة التالية:
نظرًا لأننا نستخدم واجهة برمجة تطبيقات وهمية، فإننا لا نضيف أو نحدّث أو نحذف المنشورات فعليًا، ومع ذلك، نتلقى استجابة مماثلة من الواجهة الخلفية كما تفعل مع واجهة برمجة التطبيقات الفعلية.
إذا كنت تتابع الأمر، فتأكد من تسمية الفئة أعلاه BlogPostوليس Post. أوافق على أن Postهذا اسم أفضل. ومع ذلك، سيتعارض مع فئة Post الخاصة بالحزمة ولن يعمل التنفيذ الذي تمت مناقشته في هذا القسم.
الآن بدلاً من إرجاع عادي Response، يمكننا الآن إرجاع a Responseبنوع.
import 'package:chopper/chopper.dart';
import 'package:chopper_client_generator/blog_post.dart';
part 'post_service.chopper.dart';
@ChopperApi(baseUrl: '/posts')
abstract class PostService extends ChopperService {
static PostService create([ChopperClient? client]) =>
_$PostService(client);
@Get()
Future<Response<List<BlogPost>>> posts();
@Get(path: '/{id}')
Future<Response<BlogPost?>> post(@Path() int id);
@Post()
Future<Response<BlogPost>> add(@Body() Map<String, dynamic> json);
@Put(path: '/{id}')
Future<Response<BlogPost>> update(@Path() int id, @Body() Map<String, dynamic> json);
@Delete(path: '/{id}')
Future<Response> delete(@Path() int id);
@Get(path: '/{id}/comments')
Future<Response> comments(@Path() int id);
@Get(path: 'https://jsonplaceholder.typicode.com/comments')
Future<Response> commentsWithQuery({@Query() int postId = 1});
}
في مقتطف التعليمات البرمجية أعلاه، بدلاً من إرجاع a فقط، Responseفإننا نرجع a Reponseمع قائمة من BlogPostالكائنات أو a Responseمع كائن واحد BlogPost. بهذه الطريقة، سيتم تحويل ملف JSON المستلم من واجهة برمجة التطبيقات على الفور إلى كائن Dart.
بالطبع، عندما نقوم بإجراء تغييرات على ChopperServiceفئة، يتعين علينا إعادة إنشاء ملف المروحية:
dart run build_runner build --delete-conflicting-outputs
إنشاء JsonSerializationConverter
إن مجرد تغيير أنواع الاستجابة لـ a ChopperServiceليس كافيًا. لأنه كما في السابق، يتعين علينا التأكد من تحويلها بشكل صحيح. لسوء الحظ، فإن استخدام الحزمة JsonConverterلن يؤدي الغرض. ومع ذلك، يمكننا إنشاء المحول التالي الذي سيكون قادرًا على تسلسل البيانات وإلغاء تسلسلها من واجهة برمجة التطبيقات باستخدام كائنات Dart.
import 'dart:async' show FutureOr;
import 'package:chopper/chopper.dart';
typedef JsonFactory<T> = T Function(Map<String, dynamic> json);
class JsonSerializationConverter extends JsonConverter {
const JsonSerializationConverter(this.factories);
final Map<Type, JsonFactory> factories;
T? _decodeMap<T>(Map<String, dynamic> values) {
final jsonFactory = factories[T];
if (jsonFactory == null || jsonFactory is! JsonFactory<T>) {
return null;
}
return jsonFactory(values);
}
List<T> _decodeList<T>(Iterable values) =>
values.where((v) => v != null).map<T>((v) => _decode<T>(v)).toList();
dynamic _decode<T>(entity) {
if (entity is Iterable) return _decodeList<T>(entity as List);
if (entity is Map) return _decodeMap<T>(entity as Map<String, dynamic>);
return entity;
}
@override
FutureOr<Response<ResultType>> convertResponse<ResultType, Item>(
Response response,
) async {
final jsonResponse = await super.convertResponse(response);
return jsonResponse.copyWith<ResultType>(
body: _decode<Item>(jsonResponse.body));
}
}
في هذه الحالة، JsonSerializationConverterنلغي convertResponseالوظيفة. داخل الإلغاء، نستمر في استدعاء convertResponseوظيفة الفئة الأصلية. ومع ذلك، نغير استجابة الإرجاع لإرجاع قائمة من كائنات Dart أو كائن Dart واحد.
يتم ذلك داخل _decodeالدالة. تحدد هذه الدالة بناءً على ما هو معطى jsonResponse.bodyما إذا كنا نريد إرجاع قائمة من الكائنات أو كائن واحد. بمجرد أن تحدد الدالة ما الذي يجب إرجاعه، ستتحقق داخل السمة factoriesما إذا كانت تستطيع العثور على النوع الصحيح.
في هذه الحالة، يبحث عن BlogPostالنوع. بمجرد العثور على النوع الصحيح، سيحول استجابة JSON إلى القائمة الصحيحة من BlogPostالكائنات أو BlogPostكائن واحد.
تنفيذ JsonSerializationConverter
بعد إنشاء الملف JsonSerializationConverterيجب علينا التأكد من استخداماتنا ChopperClientله.
import 'package:chopper/chopper.dart' show ChopperClient, JsonConverter;
import 'package:chopper_client_generator/blog_post.dart';
import 'package:chopper_client_generator/json_serialization_converter.dart';
import 'package:chopper_client_generator/post_service.dart';
void main() async {
final chopper = ChopperClient(
baseUrl: Uri.parse('https://jsonplaceholder.typicode.com'),
converter: const JsonSerializationConverter({BlogPost: BlogPost.fromJson}),
errorConverter: const JsonConverter(),
services: [
PostService.create(),
],
);
final postService = chopper.getService<PostService>();
try {
final response = await postService.post(5);
if (response.isSuccessful) {
final body = response.body;
print(body);
} else {
throw Exception(response.error);
}
} catch (error) {
print(error);
}
}
في الكود أعلاه، بدلاً من استخدام العادي، JsonConverterنقوم الآن بتمرير مثيل من JsonSerializationConverter. المخصص لدينا. يأخذ المحول المخصص لدينا خريطة من Typeو factoryواستخدمناها لتمرير BlogPostالنوع الخاص بنا ومصنعه.
بعد ذلك، يمكننا تشغيل mainوظيفتنا كما في السابق وسوف ترى أننا حصلنا على الإخراج التالي:
Instance of 'BlogPost'
خاتمة
في هذه المقالة، تعلمت كيفية إنشاء عملاء واجهة برمجة التطبيقات في Flutter باستخدام حزمة Chopper. قد يتطلب الإعداد الأولي بعض الوقت والمعرفة، ولكن بعد الانتهاء منه، يمكنك إعداد اتصال سريع مع الواجهة الخلفية لديك.
لقد تعلمت أيضًا كيفية تحويل بيانات JSON إلى كائنات Dart، مما يجعل التعامل مع البيانات أسهل كثيرًا. وبينما قمنا بتغطية العديد من جوانب حزمة Chopper في هذا المنشور، لا يزال هناك الكثير لنتعلمه. إذا كنت مهتمًا، فلا تتردد في مراجعة وثائقها الرسمية .