Farid
Farid

Reputation: 772

WSDL how to set target namespace independent of the service execution url

I have to set a SOAP server under PHP and I've get it working, but because I need to maintain separate states from development, stage and production, it needs to have all real urls out of the wsdl. I have search a lot of documentation but any example I got have target urls in wsdl, and I finish the following working wsdl

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="https://example.com/ws/OCService/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="OCService" targetNamespace="https://example.com/ws/OCService/">
  <wsdl:types>
    <xsd:schema targetNamespace="https://example.com/ws/OCService/">
      <xsd:element name="OrderCreation">
        <xsd:complexType>
            <xsd:annotation>
               <xsd:documentation>How to use the server</xsd:documentation>
           </xsd:annotation>
           <xsd:sequence>
             <xsd:element name="name" type="xsd:string"></xsd:element>
             <xsd:element name="number" type="xsd:int"></xsd:element>
             <xsd:element name="opCode" type="xsd:string"></xsd:element>
             <xsd:element name="providerCode" type="xsd:string"></xsd:element>
             <xsd:element name="amount" type="xsd:decimal"></xsd:element>
             <xsd:element name="currency" type="xsd:string"></xsd:element>
             <xsd:element name="term" type="xsd:date"></xsd:element>
             <xsd:element name="advance" type="xsd:decimal"></xsd:element>
             <xsd:element name="repair" type="xsd:decimal"></xsd:element>
             <xsd:element name="fortnight" type="xsd:int"></xsd:element>
             <xsd:element name="items" type="tns:orderedItems"></xsd:element>
         </xsd:sequence>
     </xsd:complexType>
 </xsd:element>
 <xsd:element name="OrderCreationResponse">
    <xsd:annotation>
       <xsd:documentation>Response: Error or Success</xsd:documentation>
   </xsd:annotation>
   <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="result" type="xsd:string"/>
    </xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="Orden">
    <xsd:annotation>
       <xsd:documentation>General information</xsd:documentation>
   </xsd:annotation>
   <xsd:sequence>
    <xsd:element name="name" type="xsd:string"></xsd:element>
    <xsd:element name="number" type="xsd:int"></xsd:element>
    <xsd:element name="opCode" type="xsd:string"></xsd:element>
    <xsd:element name="providerCode" type="xsd:string"></xsd:element>
    <xsd:element name="amount" type="xsd:float"></xsd:element>
    <xsd:element name="currency" type="xsd:string"></xsd:element>
    <xsd:element name="term" type="xsd:date"></xsd:element>
    <xsd:element name="advance" type="xsd:float"></xsd:element>
    <xsd:element name="repair" type="xsd:float"></xsd:element>
    <xsd:element name="fortnight" type="xsd:int"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Item">
    <xsd:annotation>
       <xsd:documentation>Item information</xsd:documentation>
   </xsd:annotation>
   <xsd:sequence>
       <xsd:element name="itemName" type="xsd:string" />
       <xsd:element name="number" type="xsd:int" />
       <xsd:element name="unity" type="xsd:string" />
       <xsd:element name="unitPrice" type="xsd:decimal" />
       <xsd:element name="quantity" type="xsd:decimal" />
       <xsd:element name="itemAmount" type="xsd:decimal" />
       <xsd:element name="itemOrder" type="xsd:string" />
   </xsd:sequence>
</xsd:complexType>
<xsd:complexType name="orderedItems">
    <xsd:annotation>
       <xsd:documentation>Array de items que integran la OC</xsd:documentation>
   </xsd:annotation>
   <xsd:sequence>
     <xsd:element name="item" type="tns:Item" minOccurs="0" maxOccurs="unbounded"/>
 </xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<wsdl:message name="OrderCreationRequest">
    <wsdl:part element="tns:OrderCreation" name="parameters"/>
</wsdl:message>
<wsdl:message name="OrderCreationResponse">
    <wsdl:part element="tns:OrderCreationResponse" name="parameters"/>
</wsdl:message>
<wsdl:portType name="OCService">
    <wsdl:operation name="OrderCreation">
      <wsdl:input message="tns:OrderCreationRequest"/>
      <wsdl:output message="tns:OrderCreationResponse"/>
  </wsdl:operation>
</wsdl:portType>
<wsdl:binding name="OCServiceSOAP" type="tns:OCService">
 <soap:binding style="document"
    transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="OrderCreation">
        <soap:operation
           soapAction="https://example.com/ws/OCService/" />
           <wsdl:input>
               <soap:body use="literal" />
           </wsdl:input>
           <wsdl:output>
               <soap:body use="literal" />
           </wsdl:output>
       </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name="OCService">
    <wsdl:documentation>Orders creation service</wsdl:documentation>
    <wsdl:port binding="tns:OCServiceSOAP" name="OCServiceSOAP">
      <soap:address location="https://example.com/ws/OCService/"/>
  </wsdl:port>
</wsdl:service>
</wsdl:definitions>

The point is that this design, although it works, does not satisfy the need to separate the execution urls from the WSDL sheet, so when going from stage to production I must modify the wsdl and that should not happen, because the requirement is to keep WSDL immutable.

Somebody told me that you can set the urls in the http header but I can't understand how that alternative would work.

I would greatly appreciate any help at this point. Thanks in advance.

Upvotes: 2

Views: 1067

Answers (1)

Marcel
Marcel

Reputation: 5119

I had the same problem some time ago. In a development team we discussed possible solutions. One promising approach was to work with placeholders. I assume that you have a controller where your soap server is initialized. Even before initialization, you can check what the request URL is. Based on this request URL, the WSDL can then be parsed and adjusted accordingly.

Let us assume that your service location is a placeholder.

<wsdl:service name="OCService">
    <wsdl:documentation>Orders creation service</wsdl:documentation>
    <wsdl:port binding="tns:OCServiceSOAP" name="OCServiceSOAP">
        <soap:address location="{{PLACEHOLDER}}"/>
    </wsdl:port>
</wsdl:service>

In your controller you do something like this ...

class SoapServerController
{
    ...
    public function initAction(): void
    {
        // check the request uri
        $requestUri = $_SERVER['REQUEST_URI'];

        switch ($requestUri) {
            case 'yadda' :
                $environment = 'staging';
                $locationUri = 'https://example.com/A';
                break;
            case 'blubb' :
                $environment = 'production';
                $locationUri = 'https://example.com/B';
                break;
            default :
                $environment = 'test';
                 $locationUri = 'https://example.com/C';
                 break;
        }

        // rewrite the wsdl
        $doc = new DOMDocument();
        $doc->loadXML('https://some.location.tld/inital.wsdl');

        $address = $doc->getElementsByTagNameNS('http://schemas.xmlsoap.org/wsdl/soap/', 'address');
        $adress->setAttribute('location', $locationUri);
        
        $filename = PATH_TO_WSDL_FOLDER . '/' . $environment . '.wsdl';
        $doc->save($filename);

        // is the wsdl get parameter set?
        if (isset($_GET['wsdl'])) {
            echo $doc->saveXML();
            exit();
        }

        // initialize the soap server
        $service = new SoapService();
        $server = new SoapServer($filename, []);
        $server->setObject($service);
        $server->handle();
    }
}

Please remember that this is only sample code that has not been tested. However, the team decided to create one WSDL file per environment and to abandon the logic, because writing a new WSDL file per request would be too much technical debt. Even when a cache system was used. We have opted for speed and maintainable, simple code. I don 't know how much environments you have to deal with. For us it was easier to deal with four different wsdl files.

Upvotes: 1

Related Questions