XML وSOAP
لغة XML (Extensible Markup Language)، التي تعرّفها توصية XML 1.0 الصادرة عن W3C، صيغة نصية تحفظ البيانات في عناصر مسمّاة ومتداخلة مثل <orderId>ORD-1042</orderId>. وSOAP بروتوكول مراسلة مبني عليها: كل طلب وكل استجابة وثيقة XML تُسمّى المغلف (Envelope)، ويصف ملف WSDL عمليات الخدمة وشكل XML الدقيق الذي تقبله كل عملية.
مثال: لمعرفة حالة طلب شراء، يرسل تطبيق العميل (Client) بطريقة POST مغلفاً يحمل جسمه العنصر GetOrderStatus ورقم الطلب، فيتلقى مغلفاً فيه الحالة أو عطل SOAP (SOAP Fault) يوضح ما الخطأ. وبخلاف REST، تُسمّى العملية داخل الرسالة، لا بالطريقة (Method) وعنوان URI.
لماذا يهم
ينتشر SOAP في التكامل بين المؤسسات وفي الأنظمة المؤسسية طويلة العمر، مثل واجهات المصارف والدفع والتأمين والخدمات الحكومية والاتصالات، وكثيراً ما يعمل خلف واجهة حديثة. وقد يوقف عيبٌ فيه عملية أعمال كاملة، مثل تأكيد دفعة.
كما أن أعطال XML وSOAP تختلف عن أعطال JSON عبر REST. فعلامة & واحدة غير مُهرَّبة تجعل الرسالة كلها غير مقروءة، والعنصر في مساحة أسماء خاطئة مجهول وإن بدا اسمه صحيحاً، ويعيد SOAP 1.1 رمز HTTP 500 حتى للمدخلات الخاطئة. ومن يقرأ رموز الحالة وحدها يفوّت عيوباً ويختلق عيوباً غير موجودة.
أساسيات XML: سلامة البنية والصلاحية ومساحات الأسماء
العنصر (Element) وسم بداية ثم محتوى ثم وسم نهاية، مثل <total>249.90</total>، ويمكن كتابة العنصر الخالي من المحتوى بالشكل <item/>. والسمة (Attribute) زوج name="value" داخل وسم البداية، مثل currency="SAR" في الوثيقة أدناه. ولوثيقة XML عنصر جذري (Root) واحد بالضبط، وكل عنصر يُغلق ويتداخل تداخلاً صحيحاً، والأسماء حساسة لحالة الأحرف، فـ <orderId> و<OrderId> عنصران مختلفان. وقيم السمات تُكتب بين علامتي اقتباس، ولا يتكرر اسم السمة في العنصر الواحد، ولا معنى لترتيب السمات.
اتباع هذه القواعد يجعل الوثيقة سليمة البنية (Well-formed)؛ ويعدّ XML 1.0 أي مخالفة خطأً فادحاً (Fatal error)، فيرفض المحلل (Parser) الرسالة كلها بدلاً من إصلاحها كما تفعل المتصفحات مع HTML. والوثيقة الصالحة (Valid) تطابق مخططاً أيضاً (ويعرّف XML 1.0 ذلك لملفات DTD)؛ وتستخدم خدمات الويب XML Schema (XSD) لتحديد العناصر الإلزامية وترتيبها وأنواعها وقيمها المسموحة.
- تهريب الأحرف الخاصة (Escaping): لا يجوز أن يظهر
<و&كما هما في النص؛ اكتب<و&(ويعرّف XML مسبقاً أيضاً>و"و'). ومقطع CDATA، أي<![CDATA[...]]>، يحمل نصاً دون تهريب. - ترميز الأحرف (Encoding): يحدده التصريح الاختياري
<?xml version="1.0" encoding="UTF-8"?>، والبايتات التي لا تطابقه تشوّه الأسماء العربية أو الهندية أو تؤدي إلى رفض الرسالة. - مساحات الأسماء (Namespaces):
xmlns:ord="urn:example:orders"يربط بادئة بعنوان URI. ويُعرَّف العنصر بالعنوان مع اسمه المحلي، فالبادئة لا تهم، والاسم الصحيح في مساحة أسماء خاطئة مجهول.
<?xml version="1.0" encoding="UTF-8"?>
<order id="ORD-1042" currency="SAR">
<customer>Smith & Sons</customer>
<item sku="A-100" qty="2"/>
<note><![CDATA[Leave at <b>reception</b>]]></note>
<total>249.90</total>
</order>مغلف SOAP وربطه بـ HTTP
رسالة SOAP وثيقة XML عنصرها الجذري Envelope. وتحمل الترويسة الاختيارية Header بيانات عن الرسالة مثل معرّف الطلب أو رمز الأمان (Security token)، ويحمل الجسم الإلزامي Body عنصر الطلب أو الاستجابة، أو عطلاً. وكتلة الترويسة الموسومة بـ mustUnderstand التي يعجز المستقبِل عن معالجتها يجب أن تُنتج عطل MustUnderstand.
تُرسَل الطلبات عادةً بطريقة POST إلى عنوان نقطة نهاية (Endpoint) واحد تشترك فيه كل العمليات. ويختلف إصدارا SOAP في مساحة أسماء المغلف وفي الربط بـ HTTP، لذا يفشل الطلب المبني لأحد الإصدارين عند إرساله إلى خدمة لا تدعم إلا الإصدار الآخر.
SOAPAction، ومساحة أسماء مغلف XML تحدد الإصدار، مع Header اختيارية وBody إلزامي يسمّي العملية.سطر طلب HTTP والترويسات
الجزء 1:POST /services/orders HTTP/1.1 Host: api.example.com الجزء 2:Content-Type: text/xml; charset=utf-8 الجزء 3:SOAPAction: "urn:example:orders/GetOrderStatus"
جسم XML: مغلف SOAP
الجزء 4:<?xml version="1.0" encoding="UTF-8"?> الجزء 5:<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" الجزء 6:xmlns:ord="urn:example:orders"> الجزء 7:<soap:Header> <ord:RequestId>7f3c-0091</ord:RequestId> </soap:Header> الجزء 8:<soap:Body> الجزء 9:<ord:GetOrderStatus> الجزء 10:<ord:orderId>ORD-1042</ord:orderId> </ord:GetOrderStatus> </soap:Body> </soap:Envelope>
- 1عادةً
POSTإلى نقطة نهاية واحدةكل العمليات تشترك في هذا العنوان، والعنصر داخلBodyهو ما يسمّي العملية - 2نوع الوسائط في SOAP 1.1يستخدم SOAP 1.2 النوع
application/soap+xml - 3ترويسة
SOAPAction، في SOAP 1.1 فقطيجب أن يرسلها العميل بالقيمة الواردة في ربط WSDL، ويستخدم SOAP 1.2 بدلاً منها المعاملaction - 4تصريح XMLاختياري، ويحدد ترميز الأحرف
- 5العنصر
Envelopeفي مساحة أسماء SOAPمساحة الأسماء تحدد الإصدار، وغير المدعومة منها تحصل علىVersionMismatch - 6مساحة أسماء الخدمةهي
targetNamespaceفي المخطط، ويجب أن تكون عناصر العملية فيها - 7الترويسة
Header، اختياريةبيانات عن الرسالة مثل معرّف الطلب؛ والكتلة الموسومةmustUnderstandإما أن تُعالَج وإما أن يُردّ عليها بعطل - 8الجسم
Body، إلزاميعنصر الطلب أو الاستجابة، أوFault - 9عنصر العمليةاسمه ومساحة أسمائه يحددان العملية المطلوبة
- 10معامل يُفحص وفق مخطط XSDإلزامي، ويجب أن يطابق
ORD-متبوعاً بـ1 إلى 8 أرقام
- SOAP 1.1 (مذكرة W3C Note صدرت عام 2000): مساحة الأسماء
http://schemas.xmlsoap.org/soap/envelope/، وContent-Type: text/xml، وترويسةSOAPActionإلزامية يحدد WSDL قيمتها لكل عملية. - SOAP 1.2 (توصية W3C منذ 2003): مساحة الأسماء
http://www.w3.org/2003/05/soap-envelope، وContent-Type: application/soap+xml، والإجراء (Action)، إن وُجد، معامل باسمactionفي هذه الترويسة.
النسخة النصية
POST /services/orders HTTP/1.1
Host: api.example.com
Content-Type: text/xml; charset=utf-8
SOAPAction: "urn:example:orders/GetOrderStatus"
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ord="urn:example:orders">
<soap:Header>
<ord:RequestId>7f3c-0091</ord:RequestId>
</soap:Header>
<soap:Body>
<ord:GetOrderStatus>
<ord:orderId>ORD-1042</ord:orderId>
</ord:GetOrderStatus>
</soap:Body>
</soap:Envelope>WSDL: عقد خدمة SOAP
وثيقة WSDL (Web Services Description Language) هي العقد المقروء آلياً لخدمة SOAP، ويُعتاد نشرها على عنوان نقطة النهاية مع إضافة ?wsdl. وما زال WSDL 1.1 (مذكرة من W3C صدرت عام 2001) واسع الاستخدام؛ ويغيّر WSDL 2.0 (عام 2007) اسم portType إلى interface واسم port إلى endpoint. ولـ WSDL 1.1 خمسة عناصر في المستوى الأعلى، مذكورة أدناه.
ومخطط XSD داخل types هو أدق جزء في أساس الاختبار (Test basis). ففي المخطط الوارد في نهاية هذا القسم، orderId إلزامي (القيمة الافتراضية لـ minOccurs هي 1) ويجب أن يطابق ORD- متبوعاً بـ1 إلى 8 أرقام، لأن أنماط XSD تطابق القيمة كاملة. وincludeItems اختياري لا يقبل إلا true أو false أو 1 أو 0؛ ويفرض xs:sequence الترتيب؛ وtargetNamespace هي مساحة الأسماء التي يجب أن تستخدمها عناصر الطلب.
types: تعريفات XML Schema لعناصر الطلب والاستجابة والأعطال.message: البيانات التي ترسلها كل عملية أو تستقبلها.portType: العمليات بمدخلاتها ومخرجاتها وأعطالها المعلنة.binding: الربط، أي إصدار SOAP، ونمطdocumentأوrpc، وقيمةsoapActionلكل عملية.service: عنوان المنفذ (Port)، أي عنوان URL الذي ترسل إليه الطلبات.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:example:orders"
elementFormDefault="qualified">
<xs:element name="GetOrderStatus">
<xs:complexType>
<xs:sequence>
<xs:element name="orderId">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="ORD-[0-9]{1,8}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="includeItems" type="xs:boolean" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>أعطال SOAP: أخطاء ذات بنية محددة
الطلب الذي يتعذر تنفيذه يحصل على عنصر Fault داخل Body. وفي SOAP 1.1 يحتوي على faultcode وfaultstring، وfaultactor اختيارياً، وعنصر detail الذي يجب أن يوجد إذا تعذرت معالجة محتوى Body، ويحمل بيانات خاصة بالتطبيق مثل أعطال الأعمال المعلنة في WSDL. ويسمّيها SOAP 1.2 Code (فيه Value وSubcode اختياري) وReason وNode وRole وDetail، والإلزامي منها Code وReason فقط. تحقّق من رمز العطل أولاً:
Clientفي 1.1 أوSenderفي 1.2: الرسالة خاطئة، أي معطوبة البنية أو غير صالحة أو تحمل قيمة خاطئة، وستفشل مجدداً إذا أُعيد إرسالها دون تغيير.Serverفي 1.1 أوReceiverفي 1.2: الرسالة سليمة لكن المعالجة فشلت، كأن تكون قاعدة البيانات متوقفة. وقد تنجح لاحقاً.VersionMismatch: مساحة أسماء مغلف غير مدعومة.MustUnderstand: لم تُفهم كتلة ترويسة إلزامية. ويضيف SOAP 1.2 الرمزDataEncodingUnknown.- رمز حالة HTTP: يفرض SOAP 1.1 الرمز
500لكل عطل، بما فيها أعطالClient؛ ويستخدم SOAP 1.2 الرمز400لأعطالSenderو500لبقيتها. لذا فإن500بعد مدخلات خاطئة صحيح في SOAP 1.1؛ ورمز العطل هو الحَكَم.
HTTP/1.1 500 Internal Server Error
Content-Type: text/xml; charset=utf-8
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>orderId must match ORD- followed by 1 to 8 digits</faultstring>
<detail>
<ord:ValidationError xmlns:ord="urn:example:orders">
<ord:field>orderId</ord:field>
<ord:value>1042</ord:value>
</ord:ValidationError>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>مثال تطبيقي: اختبار العملية GetOrderStatus
تعمل خدمة الطلبات بالإصدار SOAP 1.1 على بيئة اختبار يملكها الفريق. ويستخرج المختبِر الفحوص من WSDL ومخططه، لكل فحص نتيجة متوقعة، ويتأكد من سلامة بنية كل مغلف يكتبه يدوياً قبل إرساله (أداة «مدقّق XML» تفحص سلامة البنية، لا المطابقة لمخطط XSD).
ويتحقق المختبِر من الاستجابات بلغة XPath، وهي لغة استعلام لوثائق XML: فالتعبير //ord:status يختار كل عنصر status في مساحة الأسماء التي تربطها أداة الاختبار بالبادئة ord، أياً كانت البادئة التي تستخدمها الاستجابة نفسها. ولا يقارن المختبِر الاستجابة نصاً بنص أبداً.
- طلب صالح للرقم
ORD-1042: رمز HTTP200، وعنصرGetOrderStatusResponseفي مساحة أسماء الخدمة صالح وفق XSD. - طلب شراء غير موجود
ORD-99999999: العطل الذي يعلنه WSDL، مثلOrderNotFound، لا200بجسم فارغ. - رسالة معطوبة البنية، مثل
</ord:orderID>لإغلاق<ord:orderId>: عطلClient، ولا صفحة HTML أبداً. ولا يحتوي أيfaultstringعلى تتبّع مكدس (Stack trace) أو أسماء أصناف (Classes) أو SQL أو مسارات ملفات. - رسالة غير صالحة:
orderIdمحذوف، أو1042بلا البادئة، أوORD-دون أي رقم أو متبوعاً بتسعة أرقام (خارج الحدين 1 و8 مباشرة)، أوincludeItemsبالقيمةyes، أو تبديل ترتيب العنصرين: عطلClientيسمّي الحقل. وقبول أيٍّ منها يعني أن الخدمة لا تتحقق من مدخلاتها؛ فاسأل هل هذا مقصود. - المغلف: مغلف بمساحة أسماء SOAP 1.2، مرسَل مع
text/xml، يحصل علىVersionMismatch؛ وقيمةSOAPActionالتي لا تطابق الجسم لا تؤدي أبداً إلى تنفيذ عملية أخرى. - سطر
<!DOCTYPE>قبل المغلف ينبغي أن يُرفض بعطل: فإصدارا SOAP كلاهما يمنعان تصريح نوع الوثيقة (Document type declaration) في الرسالة، والمحلل الذي يقبله قد يكون معرَّضاً لهجمات الكيانات الخارجية في XML (XXE). لا تُجرِ مثل هذه الفحوص إلا على أنظمة لديك إذن باختبارها.
أخطاء شائعة
- الحكم على استجابة SOAP من رمز حالة HTTP: ففي SOAP 1.1 قد يكون
500هو الرد الصحيح على مدخلات خاطئة، وقد يحمل200خطأً أُعيد بيانات عادية. - مقارنة XML نصاً بنص أو التحقق من البادئات مثل
ns2:؛ فالرسائل المتكافئة قد تختلف في ترتيب السمات والمسافات البادئة والبادئات. - إرسال XML مكتوب يدوياً دون التأكد من سلامة بنيته، ثم الإبلاغ عن عطل
Clientالصحيح على أنه عيب. - اختبار المغلفات الصالحة فقط بدلاً من مخالفة كل عنصر إلزامي وكل نوع وكل نمط (Pattern) وكل قائمة قيم مسموحة (Enumeration)، وترتيب العناصر.
- افتراض أن الخادم يتحقق من الطلبات وفق XSD. فهذا خيار في الإعدادات: أرسل رسالة غير صالحة وانظر.
من منظور ضمان الجودة
في مشروع SOAP ابدأ من WSDL كما تبدأ من وثيقة OpenAPI: اكتب قائمة العمليات وأعطالها المعلنة، واقرأ XSD لمعرفة العناصر الإلزامية والأنواع والحدود. واتفق مبكراً على إصدار SOAP المستخدم، وهل تُفحص الطلبات وفق المخطط، وأي أخطاء الأعمال تعود أعطالاً لا استجابات عادية.
احفظ مغلفات الطلبات بيانات اختبار ذات إصدارات، مغلفاً لكل فحص. وفي تقرير الخطأ اذكر نقطة النهاية وإصدار SOAP وقيمة SOAPAction ومغلفَي الطلب والاستجابة كاملين ورمز حالة HTTP: ففي XML كثيراً ما يكون حرف واحد خاطئ أو مساحة أسماء خاطئة هو العيب كله.
تمرين عملي
راجع خمس استجابات من خدمة SOAP 1.1
أرسل زميلك خمسة طلبات إلى العملية CreateCustomer في خدمة SOAP 1.1 على بيئة الاختبار، ودوّن ما أُرسل (sent) وما عاد (got). قرّر لكل محاولة (Run) هل السلوك صحيح أم عيب، ولماذا.
Operation: CreateCustomer SOAP 1.1 test environment
Schema: name xs:string, maxLength 50, required
email xs:string, required
WSDL: declared fault DuplicateEmail
Run 1 sent: <cus:name>Smith & Sons</cus:name>, valid email
got: 200, <cus:name>Smith & Sons</cus:name> in the response
Run 2 sent: <cus:Email>sara@example.com</cus:Email> (no <cus:email>)
got: 200, customer C-381 created with no email
Run 3 sent: valid request, email already registered
got: 500, faultcode soap:Client, detail <cus:DuplicateEmail/>
Run 4 sent: valid request while the database is stopped
got: 500, faultcode soap:Client, faultstring "Invalid request"
Run 5 sent: envelope without </soap:Body>
got: 500, Content-Type text/html, page shows a Java stack trace- اكتب لكل محاولة رمز HTTP ورمز العطل والجسم المتوقعة من المخطط وWSDL وقواعد SOAP 1.1.
- صنّف كل محاولة: صحيحة أو عيب، واكتب لكل عيب عنوان تقرير خطأ من سطر واحد.
إظهار الإجابة النموذجية
المحاولة 1: صحيحة. المتوقع 200 مع إنشاء العميل: & مهرَّبة إلى & في XML وعادت دون تغيير، فالاسم المخزَّن هو Smith & Sons.
المحاولة 2: عيب. أسماء XML حساسة لحالة الأحرف، فـ <cus:Email> عنصر مجهول والعنصر الإلزامي email مفقود؛ وكان الواجب 500 مع عطل Client يسمّي email، فالخدمة لا تتحقق من الطلبات وفق مخططها. العنوان: «CreateCustomer ينشئ عميلاً بلا البريد الإلزامي».
المحاولة 3: صحيحة. يعيد SOAP 1.1 الرمز 500 لكل عطل، وClient مع التفاصيل DuplicateEmail المعلنة يخبر المستدعي بما يجب إصلاحه.
المحاولة 4: عيب. الطلب صالح، فالمتوقع 500 مع عطل Server؛ وعطل Client يطلب من المستدعين تغيير رسالة صحيحة ويوقف إعادة محاولاتهم. العنوان: «CreateCustomer يعيد عطل Client عند توقف قاعدة البيانات».
المحاولة 5: عيب. XML المعطوب يجب أن يحصل على 500 وعطل Client داخل مغلف SOAP يُرسَل بالنوع text/xml؛ وصفحة HTML تعطّل عملاء SOAP، وتتبّع المكدس يكشف تفاصيل داخلية. العنوان: «CreateCustomer يردّ على XML المعطوب بصفحة HTML فيها تتبّع مكدس».
أسئلة شائعة
هل يستحق SOAP التعلّم إذا كنت أعرف REST؟
نعم، إذا كنت ستختبر تكاملاً بين مؤسسات أو أنظمة مؤسسية طويلة العمر، حيث ما زال SOAP شائعاً. ومعظم ما تعلّمته في REST ينتقل معك، كالعقود والاختبارات السلبية وتقارير الأخطاء الدقيقة، ويضيف SOAP صرامة XML وعقد WSDL رسمياً وأعطالاً ذات بنية محددة.
ما الفرق بين XML سليم البنية وXML صالح؟
سليم البنية يعني أن الوثيقة تتبع قواعد صياغة XML: جذر واحد، وعناصر مغلقة ومتداخلة تداخلاً صحيحاً، وسمات بين علامتي اقتباس، وأحرف خاصة مهرَّبة. وصالح يعني أنها سليمة البنية وتطابق مخططاً أيضاً، مثل XSD في ملف WSDL. ويجب على المحلل رفض الوثيقة غير سليمة البنية، وقد تكون الوثيقة سليمة البنية وغير صالحة في آن واحد.
هل أستطيع اختبار خدمة SOAP بأداة REST؟
نعم. طلب SOAP هو طلب HTTP بطريقة POST وجسمه XML، فأي أداة ترسل جسماً خاماً مع ترويسات مخصصة تفي بالغرض. عليك أن تضبط Content-Type بنفسك، وSOAPAction في SOAP 1.1. وتضيف أدوات SOAP المتخصصة تسهيلات مثل بناء الطلبات من WSDL.
كيف أعرف هل تستخدم الخدمة SOAP 1.1 أم 1.2؟
انظر إلى الربط (Binding) في WSDL: ربط SOAP 1.1 يستخدم مساحة الأسماء http://schemas.xmlsoap.org/wsdl/soap/، وربط SOAP 1.2 يستخدم http://schemas.xmlsoap.org/wsdl/soap12/. وقد تقدّم الخدمة الإصدارين بمنفذ (Port) لكل منهما. وفي طلب يعمل، تخبرك بذلك أيضاً مساحة أسماء المغلف وقيمة Content-Type (text/xml أو application/soap+xml).
المراجع
- Extensible Markup Language (XML) 1.0 (Fifth Edition)ما يجعل مستند XML سليم البنية: العناصر والسمات والكيانات وترميز المحارف والمقدمة.
- SOAP Version 1.2 Part 1: Messaging Framework (Second Edition)إطار الرسائل في SOAP 1.2: مغلّف XML بترويسة اختيارية وجسم إلزامي، وعنصر الخطأ (Fault)، ونموذج المعالجة عبر الوسطاء.
- OWASP Cheat Sheet Series: Web Service Securityتأمين خدمات الويب SOAP وXML: أمن النقل وسلامة الرسائل والتحقق من المخطط والهجمات الخاصة بـ XML.
- OWASP Cheat Sheet Series: XML External Entity Preventionلماذا يجب على محللات XML تعطيل الكيانات الخارجية وDTD، وكيف تُضبط المحللات الشائعة بأمان.
تدرّب بأدوات متقن
أرسل مغلّفات SOAP 1.1 / 1.2 مع ترويسات SOAPAction واعرض استجابة XML منسّقة.
التحقق من سلامة بنية XML والإبلاغ عن الأخطاء مع رقم السطر والعمود.
تنسيق XML بشكل أنيق مع مسافة بادئة قابلة للتخصيص، وإعادة تصغيره عند الحاجة.
تحويل XML إلى JSON مع الحفاظ على السمات والعقد النصية وتحويل العناصر المتكررة إلى مصفوفات.
للتعمّق أكثر
تقدّمك في هذا الموضوع
سجّل الدخول لتتابع تقدّمك
علّم الموضوعات مكتملة، وتابع من حيث توقفت، وافتح امتحانات التخصص.