إرسال الطلبات ومعالجة الاستجابات
إرسال الطلبات ومعالجة الاستجابات
قدّمت الدرسُ السابق HttpClient وطريقة بنائه. الآن نضعه في العمل: نُنشئ طلبات GET وPOST، ونُرفق الترويسات، ونوفّر جسم الطلب، ونُفسّر HttpResponse الذي نتلقّاه بشكل صحيح. هذه هي الميكانيكيات اليومية لأي تكامل يعتمد على HTTP.
بناء HttpRequest
كل استدعاء HTTP يبدأ بـ HttpRequest يُبنى عبر واجهته البنّائة (fluent builder). الحد الأدنى الذي توفّره هو URI وأسلوب HTTP؛ كل شيء آخر اختياري لكنه كثيرًا ما يكون ضروريًا في الواقع العملي.
إرسال طلب GET
مرّر الطلب ومعالج الجسم (body handler) إلى client.send(). يُخبر معالج الجسم العميلَ كيف يحوّل البايتات الخام للاستجابة إلى النوع الذي تريده:
تشمل معالجات الجسم المدمجة الشائعة: ofString() وofBytes() وofFile(Path) وofInputStream() وdiscarding() (للاستجابات التي لا يهمّنا جسمها). اختر الأنسب لما ستفعله بالبيانات — بث تنزيل ضخم إلى ملف بـ ofFile() يتجنّب تحميل جيجابايتات في ذاكرة الكومة.
قراءة بيانات وصف الاستجابة
تكشف HttpResponse<T> أكثر من مجرد الجسم:
إرسال طلب POST مع جسم JSON
تُوفّر طلبات POST ناشرَ جسم (body publisher) يُسلسل حمولتك إلى بايتات يمكن للعميل بثّها إلى الخادم. الحالة الأكثر شيوعًا هي نص JSON:
لطلب POST بدون جسم (نادر لكن صالح، مثل تشغيل نقطة نهاية action)، استخدم HttpRequest.BodyPublishers.noBody().
ناشرات الجسم المدمجة الأخرى
BodyPublishers.ofString(String)— نص مُرمَّز بـ UTF-8؛ العمل اليومي لـ JSON.BodyPublishers.ofByteArray(byte[])— بايتات خام؛ مفيد للبروتوكولات الثنائية أو البيانات المُسلسلة مسبقًا.BodyPublishers.ofFile(Path)— يبثّ ملفًا مباشرة من القرص دون تحميله في الذاكرة؛ مثالي لنقاط نهاية رفع الملفات.BodyPublishers.ofInputStream(Supplier<InputStream>)— يوفّر مجرى إدخال بشكل كسول؛ يدعم الحمولات الضخمة أو المُولَّدة ديناميكيًا.BodyPublishers.noBody()— يُشير إلى جسم فارغ صراحةً (Content-Length: 0).
التعامل مع أكواد حالة HTTP باحترافية
يُعرّف HTTP أكواد الحالة في خمس فئات. فهمُ الفئات — لا مجرد حفظ الأرقام المفردة — هو ما يُمكّنك من كتابة معالجة أخطاء متينة:
- 1xx إعلامية — إشارات على مستوى البروتوكول (مثل
100 Continue). يتعامل معها العميل تلقائيًا؛ نادرًا ما تراها. - 2xx نجاح —
200 OK(GET/PUT)،201 Created(POST)،204 No Content(DELETE بدون جسم). - 3xx إعادة توجيه — يتبع
HttpClientإعادات التوجيه تلقائيًا عند ضبطه بـfollowRedirects(HttpClient.Redirect.NORMAL). - 4xx خطأ العميل — الطلب خاطئ.
400 Bad Request،401 Unauthorized،403 Forbidden،404 Not Found،409 Conflict،429 Too Many Requests. - 5xx خطأ الخادم — الخادم فشل.
500 Internal Server Error،502 Bad Gateway،503 Service Unavailable.
مساعد موجز يُرسّخ هذا المنطق:
BodyHandlers.ofString() لطلب DELETE يُعيد 204، فإن response.body() ستكون سلسلة فارغة — لا null. تمرير سلسلة فارغة لمحلل JSON سيرفع استثناءً. تحقّق من كود الحالة قبل إلغاء التسلسل، أو استخدم BodyHandlers.discarding() حين تعلم أن الجسم سيكون فارغًا.
ضبط الترويسات بشكل صحيح
تقع الترويسات في عدة فئات تستحق المعرفة الصريحة:
- Content-Type — يصف الجسم الذي ترسله. اضبطه دائمًا في POST/PUT/PATCH. أشيع القيم
application/jsonوapplication/x-www-form-urlencoded. - Accept — يُخبر الخادم بالصيغة التي يمكنك تحليلها. توفير
application/jsonصريح ويتجنّب المفاجآت مع واجهات برمجية قد تُعيد XML أو HTML. - Authorization — تحمل بيانات الاعتماد. استخدم
Bearer <token>لـ OAuth 2.0 / JWT، أوBasic <base64>لـ HTTP Basic auth. لا تمرّر بيانات الاعتماد كمعاملات استعلام قط. - User-Agent — يُعرّف عميلك. بعض الواجهات البرمجية تحدّ المعدل أو تحجب الطلبات التي لا تحمل سلسلة user agent ذات معنى.
- Idempotency-Key (مثل Stripe) — UUID فريد تولّده لكل عملية منطقية حتى يتمكّن الخادم من إلغاء تكرار طلبات POST المُعاد محاولتها بأمان.
الخلاصة
أنت الآن تعرف كيف تصنع طلبات GET وPOST، وتُرفق ترويسات تعسّفية، وتختار ناشر الجسم المناسب لحمولتك، وتقرأ كود الحالة والترويسات والجسم من الاستجابة. هذه هي اللبنات التي يتألّف منها كل تفاعل HTTP في Java — أنماط العمل غير المتزامن وعملاء REST في الدروس القادمة تبني مباشرة على هذا الأساس.