استخدام SOAP-to-REST لتبسيط الهجرة والتكامل

Jinhua Luo

March 31, 2023

Technology

1. ما هي خدمة الويب؟

تُعرّف خدمة الويب من قبل اتحاد شبكة الويب العالمية (W3C) كنظام برمجي مصمم لدعم التفاعل القابل للتشغيل المتبادل بين الآلات عبر الشبكة.

تقوم خدمة الويب بتنفيذ مهام محددة أو مجموعة من المهام ويتم وصفها بواسطة تمثيل XML قياسي للخدمة يُسمى لغة وصف خدمات الويب (WSDL). يوفر وصف الخدمة جميع التفاصيل اللازمة للتفاعل مع الخدمة، بما في ذلك تنسيقات الرسائل (المستخدمة لوصف العمليات بالتفصيل)، بروتوكولات النقل، والموقع.

تتفاعل الأنظمة الأخرى مع خدمة الويب باستخدام رسائل SOAP، عادةً باستخدام HTTP بالتزامن مع تسلسل XML ومعايير أخرى متعلقة بالويب.

مخطط بنية خدمة الويب (لاحظ أن وسيط الخدمة اختياري) هو:

بنية خدمات الويب

*مصدر الصورة (مرخصة تحت CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_service

تخفي واجهة WSDL المعلومات التفصيلية حول كيفية تنفيذ الخدمة بحيث يكون استخدام الخدمة مستقلًا عن النظام الأساسي للأجهزة أو البرامج التي تنفذ الخدمة، وكذلك لغة البرمجة المستخدمة لكتابة الخدمة.

تطبيقات خدمات الويب مبنية على مكونات مترابطة بشكل فضفاض، موجهة نحو المكونات، وتنفيذ عبر التقنيات. يمكن استخدام خدمات الويب بشكل فردي أو مع خدمات ويب أخرى لتنفيذ تجميعات معقدة أو معاملات تجارية.

خدمات الويب هي وحدات تنفيذ هندسة الخدمات الموجهة (SOA)، وهي طريقة تصميم تُستخدم لاستبدال الأنظمة الأحادية. يمكن تقسيم نظام واسع إلى خدمات ويب متعددة، والتي يمكن بعد ذلك دمجها في صندوق أسود كبير لتوفير المنطق التجاري خارجيًا. الخدمات الصغيرة القائمة على الحاويات، هي أحدث بديل لخدمات الويب. ومع ذلك، فإن العديد من الأنظمة القديمة تعتمد بالفعل على خدمات الويب للتنفيذ والتشغيل، لذا فإن التوافق مع هذه الأنظمة يظل تحديًا على الرغم من التطور السريع للتكنولوجيا.

WSDL (لغة وصف خدمات الويب)

WSDL هي ترميز XML يُستخدم لوصف خدمات الويب. يوجه تعريف WSDL العملاء حول كيفية كتابة طلبات خدمة الويب ويحدد الواجهة التي يوفرها موفر خدمة الويب.

ينقسم تعريف WSDL إلى أجزاء متعددة، تحدد الواجهة المنطقية للخدمة والتفاصيل المادية. تشمل التفاصيل المادية معلومات النقطة الطرفية، مثل رقم منفذ HTTP، بالإضافة إلى معلومات الربط التي تحدد كيفية تمثيل محتوى رسالة SOAP وأي طريقة نقل تُستخدم.

تمثيل المفاهيم المحددة بواسطة وثائق WSDL 1.1 و WSDL 2.0.

مصدر الصورة (مرخصة تحت CC 3.0 BY-SA): https://en.wikipedia.org/wiki/Web_Services_Description_Language

  • يمكن أن يحتوي ملف WSDL على خدمات متعددة.
  • يمكن أن تحتوي الخدمة على منافذ متعددة.
  • يُعرّف المنفذ عنوان URL (والذي يمكن أن يختلف لكل منفذ) ويمكن أن يحتوي على عمليات متعددة.
  • تتضمن كل عملية نوع إدخال ونوع إخراج.
  • تحدد الأنواع بنية الرسالة، بما في ذلك الحقول التي تتكون منها الرسالة، نوع بيانات كل حقل (والذي يمكن أن يكون متداخلاً)، وعدد الحقول المسموح بها.

1.1 ما هو SOAP

SOAP هو تنسيق رسالة XML يُستخدم في تفاعلات خدمة الويب. تُرسل رسائل SOAP عادةً عبر HTTP أو JMS، ولكن يمكن أيضًا استخدام بروتوكولات نقل أخرى. يصف تعريف WSDL استخدام SOAP في خدمة ويب محددة.

هناك نسختان شائعتان من SOAP: SOAP 1.1 و SOAP 1.2.

بنية SOAP

مصدر الصورة (مرخصة تحت CC 3.0 BY-SA): https://en.wikipedia.org/wiki/SOAP

تحتوي رسالة SOAP على الأجزاء التالية:

  • بيانات وصفية للرأس، والتي تكون عادةً فارغة
  • الجسم
    • نوع الرسالة المحدد في WSDL
    • بالإضافة إلى الاستجابات الناجحة، هناك أيضًا رسائل خطأ منظمة لأنواع الاستجابة.

على سبيل المثال:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header></SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <ns2:getCountryResponse xmlns:ns2="http://spring.io/guides/gs-producing-web-service">
      <ns2:country>
        <ns2:name>Spain</ns2:name>
        <ns2:population>46704314</ns2:population>
        <ns2:capital>Madrid</ns2:capital>
        <ns2:currency>EUR</ns2:currency>
      </ns2:country>
    </ns2:getCountryResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

1.2 ما هو REST

خدمة الويب هي مفهوم مجرد يمكن تنفيذه بأي طريقة؛ على سبيل المثال، REST هي طريقة تنفيذ شائعة.

REST، اختصار لنقل حالة التمثيل، يعني حرفيًا نقل حالة طبقة العرض. اقترح Roy Thomas Fielding مصطلح REST في أطروحته للدكتوراه عام 2000. في ذلك الوقت، كانت الإنترنت في ازدهار، وكانت هناك حاجة إلى تعريف عملي لتطوير البرمجيات والتفاعل مع الشبكات.

لفترة طويلة، ركزت أبحاث البرمجيات بشكل رئيسي على تصنيف تصميم البرمجيات وتطور طرق التصميم. نادرًا ما تم تقييم تأثير خيارات التصميم المختلفة على سلوك النظام بشكل موضوعي. من ناحية أخرى، ركزت أبحاث الشبكات بشكل رئيسي على تفاصيل سلوك الاتصال بين الأنظمة وكيفية تحسين أداء آليات الاتصال المحددة، وغالبًا ما تم تجاهل أن تغيير نمط تفاعل التطبيق له تأثير أكبر على الأداء العام من تغيير بروتوكول التفاعل. تهدف مقالتي إلى فهم وتقييم تصميم بنية برمجيات التطبيقات القائمة على الشبكة والتي تكون وظيفية، وتعمل بشكل جيد، وتناسب الاتصال مع الامتثال لمبادئ البنية.

الوصول إلى موقع ويب يمثل عملية تفاعل بين العميل والخادم. خلال هذه العملية، يجب أن تكون هناك تغييرات في البيانات والحالة. بروتوكول HTTP هو بدون حالة، مما يعني أن جميع الحالات مخزنة على جانب الخادم. لذلك، إذا أراد العميل تشغيل الخادم، يجب أن يستخدم بعض الوسائل لإحداث "نقل حالة" على جانب الخادم. نظرًا لأن هذا النقل مبني على طبقة العرض، يُسمى "نقل حالة طبقة العرض."

المبادئ الأساسية الأربعة لـ REST هي:

  1. استخدام أفعال HTTP: GET، POST، PUT، DELETE؛
  2. اتصال بدون حالة، يجب ألا يخزن الخادم الكثير من الحالة السياقية؛ أي أن كل طلب مستقل؛
  3. تعيين URI لكل مورد؛
  4. استخدام x-www-form-urlencoded أو JSON كتنسيق للبيانات؛

تحويل SOAP إلى REST يمكن أن يسهل على المستخدمين الوصول إلى خدمات الويب التقليدية بطريقة RESTful، مما يقلل من تكلفة تطوير عملاء SOAP. علاوة على ذلك، إذا كان يمكن التكيف الديناميكي مع أي خدمة ويب بدون تطوير برمجي، سيكون ذلك أكثر مثالية.

أكبر ميزة لـ REST هي أنها لا تحتوي على مخطط، مما يجعل التطوير مريحًا، و JSON لديه قابلية قراءة أعلى وتكرار أقل.

2. التنفيذات التقليدية لوكيل SOAP-to-REST

2.1 التحويل اليدوي للنماذج

يتطلب هذا النهج توفير قوالب تحويل للطلب والاستجابة لكل عملية من عمليات خدمة الويب، وهو ما تستخدمه أيضًا العديد من منتجات البوابات الأخرى.

يمكننا استخدام مكون APISIX body transformer لإنشاء وكيل SOAP-to-REST بسيط وتجربة هذا النهج.

على سبيل المثال، نقوم ببناء نموذج طلب بتنسيق XML لعملية getCountry لخدمة CountriesPortService في ملف WSDL بناءً على تعريف النوع.

هنا، نملأ حقل الاسم في JSON في حقل الاسم في getCountryRequest.

req_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
<?xml version="1.0"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
 <soap-env:Body>
  <ns0:getCountryRequest xmlns:ns0="http://spring.io/guides/gs-producing-web-service">
   <ns0:name>{{_escape_xml(name)}}</ns0:name>
  </ns0:getCountryRequest>
 </soap-env:Body>
</soap-env:Envelope>
EOF
)

يجب توفير نموذج XML-to-JSON للاستجابة، والذي يمكن أن يكون أكثر تعقيدًا (خاصة إذا كان يجب مراعاة اختلافات إصدار SOAP). هذا لأنه يجب تحديد ما إذا كانت الاستجابة ناجحة:

  • بالنسبة للاستجابة الناجحة، يمكن تعيين الحقول مباشرة إلى JSON.
  • بالنسبة للاستجابة الفاشلة (أي الخطأ)، هناك حاجة إلى بنية JSON منفصلة، ويجب تحديد ما إذا كانت هناك حقول اختيارية محددة موجودة.
rsp_template=$(cat <<EOF | awk '{gsub(/"/,"\\\"");};1' | awk '{$1=$1};1' | tr -d '\r\n'
{% if Envelope.Body.Fault == nil then %}
{
   "currency":"{{Envelope.Body.getCountryResponse.country.currency}}",
   "population":{{Envelope.Body.getCountryResponse.country.population}},
   "capital":"{{Envelope.Body.getCountryResponse.country.capital}}",
   "name":"{{Envelope.Body.getCountryResponse.country.name}}"
}
{% else %}
{
   "message":{*_escape_json(Envelope.Body.Fault.faultstring[1])*},
   "code":"{{Envelope.Body.Fault.faultcode}}"
   {% if Envelope.Body.Fault.faultactor ~= nil then %}
   , "actor":"{{Envelope.Body.Fault.faultactor}}"
   {% end %}
}
{% end %}
EOF
)

تكوين توجيه APISIX وإجراء الاختبار:

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: xxx' -X PUT -d '
{
    "methods": ["POST"],
    "uri": "/ws/getCountry",
    "plugins": {
        "body-transformer": {
            "request": {
                "template": "'"$req_template"'"
            },
            "response": {
                "template": "'"$rsp_template"'"
            }
        }
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "localhost:8080": 1
        }
    }
}'
curl -s http://127.0.0.1:9080/ws/getCountry \
    -H 'content-type: application/json' \
    -X POST -d '{"name": "Spain"}' | jq
{
  "currency": "EUR",
  "population": 46704314,
  "capital": "Madrid",
  "name": "Spain"
}
# استجابة الخطأ
{
  "message": "Your name is required.",
  "code": "SOAP-ENV:Server"
}

كما نرى، تتطلب هذه الطريقة فهمًا يدويًا لتعريف كل عملية في ملف WSDL وعنوان خدمة الويب المقابل لكل عملية. إذا كان ملف WSDL كبيرًا ويحتوي على عدد كبير من العمليات وتعريفات الأنواع المتداخلة المعقدة، فإن هذا النهج يمكن أن يكون مرهقًا، صعب التصحيح، وعرضة للأخطاء.

2.2 Apache Camel

https://camel.apache.org/

Camel هو إطار عمل تكامل Java معروف يُستخدم لتنفيذ خطوط أنابيب التوجيه التي تحول البروتوكولات المختلفة والمنطق التجاري، و SOAP-to-REST هو مجرد حالة استخدام واحدة من حالات استخدامه.

يتطلب استخدام Camel تنزيل واستيراد ملف WSDL، وإنشاء كود stub لعميل SOAP، وكتابة كود Java:

  • تعريف نقاط نهاية REST
  • تعريف مسارات تحويل البروتوكول، مثل كيفية تعيين حقول JSON إلى حقول SOAP

كمثال، دعونا ننظر إلى خدمة ويب لتحويل وحدات درجة الحرارة:

https://apps.learnwebservices.com/services/tempconverter?wsdl

  1. إنشاء كود عميل SOAP بناءً على ملف WSDL باستخدام Maven.

سيقوم cxf-codegen-plugin بإنشاء نقاط نهاية عميل SOAP لنا للوصول إلى خدمة الويب.

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-codegen-plugin</artifactId>
      <executions>
        <execution>
          <id>generate-sources</id>
          <phase>generate-sources</phase>
          <configuration>
            <wsdlOptions>
              <wsdlOption>
                <wsdl>src/main/resources/TempConverter.wsdl</wsdl>
              </wsdlOption>
            </wsdlOptions>
          </configuration>
          <goals>
            <goal>wsdl2java</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>
  1. كتابة bean عميل SOAP.

لاحظ أننا نتذكر اسم bean كـ cxfConvertTemp، والذي سيتم استخدامه عند تعريف مسارات Camel لاحقًا.

import com.learnwebservices.services.tempconverter.TempConverterEndpoint;
@Configuration
public class CxfBeans {
    @Value("${endpoint.wsdl}")
    private String SOAP_URL;
    @Bean(name = "cxfConvertTemp")
    public CxfEndpoint buildCxfEndpoint() {
        CxfEndpoint cxf = new CxfEndpoint();
        cxf.setAddress(SOAP_URL);
        cxf.setServiceClass(TempConverterEndpoint.class);
        return cxf;
    }
}
  1. كتابة مسار REST المصب أولاً.

من هذا المسار، يمكننا أن نرى أنه يعرّف عناوين URL ذات نمط RESTFul وتعريفات معلماتها، ويعرّف المسار التالي لكل عنوان URL. على سبيل المثال، يأخذ /convert/celsius/to/fahrenheit/{num} الجزء الأخير من عنوان URL كمعامل (من نوع double) ويقدمه إلى المسار التالي direct:celsius-to-fahrenheit.

rest("/convert")
    .get("/celsius/to/fahrenheit/{num}")
    .consumes("text/plain").produces("text/plain")
    .description("Convert a temperature in Celsius to Fahrenheit")
    .param().name("num").type(RestParamType.path).description("Temperature in Celsius").dataType("int").endParam()
    .to("direct:celsius-to-fahrenheit");
  1. أخيرًا، كتابة مسار SOAP المصب والتحويل بين المصب والمصب.
from("direct:celsius-to-fahrenheit")
    .removeHeaders("CamelHttp*")
    .process(new Processor() {
        @Override
        public void process(Exchange exchange) throws Exception {
            // تهيئة طلب SOAP
            // ملء المعامل المصب num في الجسم، وهو من نوع double بسيط.
            CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest();
            c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString()));
            exchange.getIn().setBody(c);
        }
    })
    // تحديد عملية SOAP ومساحة الاسم
    // محدد في ملف application.properties
    .setHeader(CxfConstants.OPERATION_NAME, constant("{{endpoint.operation.celsius.to.fahrenheit}}"))
    .setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}"))
    // إرسال الحزمة باستخدام bean عميل SOAP الذي تم إنشاؤه بواسطة WSDL.
    .to("cxf:bean:cxfConvertTemp")
    .process(new Processor() {
        @Override
        public void process(Exchange exchange) throws Exception {
            // معالجة استجابة SOAP
            // ملء الجسم، وهو قيمة من نوع double، في السلسلة
            // إرجاع السلسلة إلى المصب
            MessageContentsList response = (MessageContentsList) exchange.getIn().getBody();
            CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0);
            exchange.getIn().setBody("Temp in Farenheit: " + r.getTemperatureInFahrenheit());
        }
    })
    .to("mock:output");
  1. الاختبار
curl localhost:9090/convert/celsius/to/fahrenheit/50
Temp in Farenheit: 122.0

كما نرى، يتطلب استخدام Camel لـ SOAP-to-REST تعريف المسارات ومنطق التحويل لجميع العمليات باستخدام كود Java، مما يتطلب تكلفة تطوير.

وبالمثل، إذا كان ملف WSDL يحتوي على العديد من الخدمات والعمليات، فإن استخدام Camel للوكالة يمكن أن يكون مؤلمًا أيضًا.

2.3 الخلاصة

دعونا نلخص عيوب الطرق التقليدية.

الوحدةCamel
WSDLتحليل يدويإنشاء كود عبر Maven
المصبتحليل يدويتحويل تلقائي
تعريف الجسمتوفير قوالب للتحويل والحكمكتابة كود التحويل
الحصول على المعلماتمتغيرات Nginxتعريف في الكود أو استدعاء واجهة عميل SOAP للحصول عليها

كلتا الطريقتين تتطلبان تكلفة تطوير، ولكل خدمة ويب جديدة، يجب تكرار هذه التكلفة.

تكلفة التطوير تتناسب مع تعقيد خدمة الويب.

3. وكيل SOAP-to-REST من APISIX

تتطلب طرق الوكالة التقليدية إما توفير قوالب تحويل أو كتابة كود تحويل، وكلاهما يتطلب من المستخدمين تحليل ملفات WSDL بعمق ويتطلب تكلفة تطوير كبيرة.

يوفر APISIX نهجًا تلقائيًا يقوم بتحليل ملفات WSDL تلقائيًا ويوفر منطق التحويل لكل عملية، مما يلغي تكلفة التطوير للمستخدمين.

هذا مكون Enterprise، اتصل بنا لمزيد من المعلومات.

وكيل SOAP-to-REST من APISIX

3.1 التحويل التلقائي بدون كود

باستخدام وكيل SOAP من APISIX:

  • لا يتطلب تحليل أو استيراد ملفات WSDL يدويًا
  • لا حاجة لتحديد قوالب التحويل
  • لا حاجة لكتابة أي كود تحويل أو اقتران.

يحتاج المستخدمون فقط إلى تكوين عنوان URL لـ WSDL، وسيقوم APISIX بالتحويل تلقائيًا. وهو مناسب لأي خدمة ويب، وهو برنامج عام ولا يتطلب تطويرًا ثانويًا لاحتياجات محددة.

3.2 التكوين الديناميكي

  • يمكن ربط عنوان URL لـ WSDL بأي مسار، ومثل كائنات الموارد الأخرى في APISIX، يمكن تحديثه أثناء التشغيل. التغييرات في التكوين تصبح فعالة ديناميكيًا دون الحاجة إلى إعادة تشغيل APISIX.
  • سيتم التعرف تلقائيًا على عنوان URL للخدمة (الخدمات) المضمنة في ملف WSDL (والتي قد تحتوي على عناوين URL متعددة)، أي عنوان المصب، واستخدامها كمصدر SOAP دون الحاجة إلى تحليلها وتكوينها من قبل المستخدمين.

3.3 آلية التنفيذ

  • الحصول على محتوى ملف WSDL من عنوان URL لـ WSDL وإنشاء كائن وكيل تلقائيًا بعد التحليل.
  • كائن الوكيل مسؤول عن تحويل البروتوكول:
    • إنشاء طلبات SOAP XML متوافقة بناءً على إدخال JSON.
    • تحويل استجابات SOAP XML إلى استجابات JSON.
    • الوصول إلى خدمة الويب والتعامل تلقائيًا مع تفاصيل بروتوكول SOAP، مثل استجابات الخطأ.
    • دعم SOAP1.1 و SOAP1.2 وعدة ميزات توسيعية، مثل WS-Addressing.

3.4 مثال على التكوين

شرح معلمات التكوين لمكون SOAP:

المعلمةمطلوب؟الوصف
wsdl_urlنعمعنوان URL لـ WSDL، على سبيل المثال، https://apps.learnwebservices.com/services/tempconverter?wsdl

الاختبار:

# تكوين توجيه APISIX باستخدام مكون SOAP
# لاحظ أن مسارًا واحدًا يمكنه تنفيذ جميع العمليات، مع تحديد اسم العملية باستخدام معلمات URL
# يعكس هذا أيضًا فوائد الوكيل الديناميكي، مما يلغي الحاجة إلى التحليل اليدوي لكل عملية في ملف WSDL.
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
    -H 'X-API-KEY: xxx' -X PUT -d '
{
    "methods": ["POST"],
    "uri": "/getCountry",
    "plugins": {
        "soap": {
            "wsdl_url": "http://localhost:8080/ws/countries.wsdl"
        }
    }
}'
curl 'http://127.0.0.1:9080/getCountry' \
    -X POST -d '{"name": "Spain"}'
# نجاح الاستدعاء
HTTP/1.1 200 OK
Date: Tue, 06 Dec 2022 08:07:48 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.99.0
{"currency":"EUR","population":46704314,"capital":"Madrid","name":"Spain"}
# فشل الاستدعاء
HTTP/1.1 502 Bad Gateway
Date: Tue, 03 Jan 2023 13:43:33 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.99.0
{"message":"Your name is required.","actor":null,"code":"SOAP-ENV:Server","subcodes":null,"detail":null}

4. الخلاصة

في عالم اليوم، تقدمت خدمات الويب إلى النقطة التي يعتمد فيها العديد من المستخدمين المؤسسيين على خدمات الويب التقليدية القائمة على SOAP لتقديم خدماتهم. بسبب الأسباب التاريخية واعتبارات التكلفة، لا تكون هذه الخدمات دائمًا مناسبة لإعادة هيكلة كاملة إلى خدمات RESTful. نتيجة لذلك، هناك طلب كبير على SOAP-to-REST بين العديد من المستخدمين المؤسسيين.

يوفر مكون SOAP-to-REST من APISIX وظيفة وكيل بدون كود، يمكن تكوينها ديناميكيًا، ولا تتطلب تطويرًا ثانويًا، مما يفيد المستخدمين المؤسسيين في هجرة ودمج الأعمال بتكلفة صفرية.

Tags: