Alejandro Mosquera
Alejandro Mosquera

Reputation: 331

Soap server using node js

I'm trying to create a soap service with node js. It seems like the most common to do this is to use this lib: https://www.npmjs.com/package/soap

They have this snippet:

var myService = {
  MyService: {
      MyPort: {
          MyFunction: function(args) {
              return {
                  name: args.name
              };
          },

          // This is how to define an asynchronous function.
          MyAsyncFunction: function(args, callback) {
              // do some work
              callback({
                  name: args.name
              });
          },

          // This is how to receive incoming headers
          HeadersAwareFunction: function(args, cb, headers) {
              return {
                  name: headers.Token
              };
          },

          // You can also inspect the original `req`
          reallyDetailedFunction: function(args, cb, headers, req) {
              console.log('SOAP `reallyDetailedFunction` request from ' + req.connection.remoteAddress);
              return {
                  name: headers.Token
              };
          }
      }
  }
 };

  var xml = require('fs').readFileSync('myservice.wsdl', 'utf8');

 //http server example
 var server = http.createServer(function(request,response) {
  response.end('404: Not Found: ' + request.url);
 });

 server.listen(8000);
 soap.listen(server, '/wsdl', myService, xml);

 //express server example
 var app = express();
 //body parser middleware are supported (optional)
 app.use(bodyParser.raw({type: function(){return true;}, limit: '5mb'}));
 app.listen(8001, function(){
     //Note: /wsdl route will be handled by soap module
    //and all other routes & middleware will continue to work
    soap.listen(app, '/wsdl', myService, xml);
});

my question is. Do I need generate this file: myservice.wsdl manually and later link it with the structure MyService?

Thanks

Upvotes: 6

Views: 20381

Answers (2)

Fitsch
Fitsch

Reputation: 11

Ranjeeth answer is correct. At the end, the soap:address is wrong, but not only the hostname is wrong ... also the path!!!

It should be for the above example:

<soap:address location="http://localhost:8001/wsdl/" />

... AND the wsdl itself does not match up with the coding -> name of the parameters (for request: name="testParam" & response name="status") are wrong in above example. They should be both "name":

...
    <message name="MyFunctionRequest">
        <part name="name" type="xsd:string"/>
    </message>
    <message name="MyFunctionResponse">
        <part name="name" type="xsd:string"/>
    </message>
...

Just for your info: the WSDL is accessible - as shown in the soap client code - with the URL:

http://localhost:8001/wsdl?wsdl

However, I think it was unfortunate to use /wsdl as a path in the example. When using /MyFunction, it gets clearer for everybody. Here is the working example that I created for me now:

myservice.wsdl:

<definitions name = "MyService"
             targetNamespace = "http://www.examples.com/wsdl/MyService.wsdl"
             xmlns = "http://schemas.xmlsoap.org/wsdl/"
             xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:tns = "http://www.examples.com/wsdl/MyService.wsdl"
             xmlns:xsd = "http://www.w3.org/2001/XMLSchema">
    <message name="MyFunctionRequest">
        <part name="name" type="xsd:string"/>
    </message>
    <message name="MyFunctionResponse">
        <part name="name" type="xsd:string"/>
    </message>
    <portType name="MyPort">
        <operation name="MyFunction">
            <input message="tns:MyFunctionRequest"/>
            <output message="tns:MyFunctionResponse"/>
        </operation>
    </portType>
    <binding name = "MyFunction_Binding" type="tns:MyPort">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="MyFunction">
            <soap:operation soapAction="MyFunction"/>
            <input>
                <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:examples:MyService" use="encoded"/>
            </input>
            <output>
                <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:examples:MyService" use="encoded"/>
            </output>
        </operation>
    </binding>
    <service name="MyService">
        <documentation>WSDL File for MyService</documentation>
        <port binding="tns:MyFunction_Binding" name="MyPort">
            <soap:address location = "http://localhost:8001/MyFunction/" />
        </port>
    </service>
</definitions>

server using express:

const express = require('express');
const soap = require('soap');
//const bodyParser = require('body-parser');

var myService = {
      MyService: {
          MyPort: {
              MyFunction: function(args) {
                  return {
                      name: args.name
                  };
              }
          }
      }
};

var xml = require('fs').readFileSync('myservice.wsdl', 'utf8');

var app = express();

//body parser middleware are supported (optional)
//app.use(bodyParser.raw({type: function(){return true;}, limit: '5mb'}));

app.listen(8001, function(){
  //Note: /MyFunction route will be handled by soap module
  //and all other routes & middleware will continue to work
  soap.listen(app, '/MyFunction', myService, xml, function() {
    console.log('server initialized... open http://localhost:8001/MyFunction?wsdl');
  });
});

Then the soap client:

var soap = require('soap');
var url = 'http://localhost:8001/MyFunction?wsdl';
var args = { name: 'value'};
soap.createClient(url, function(err, client) {
  if(err != null) {
    console.log("client create error: ", err);
  }

  if(client != null) {
    //console.log(client.describe());
    client.MyFunction({ name: 'Phil'}, function(err, result) {
        console.log("result: ", result);
    });
  }
});

Other than that, I can recommend the github repository for this module - have a look in the /test section. Everything got clear to me while looking at:

https://github.com/vpulim/node-soap/blob/master/test/express-server-test.js

Cheers,

Phil

Upvotes: 1

Terry Lennox
Terry Lennox

Reputation: 30715

Yes, you need to create the WSDL file yourself. I believe any of the available npm SOAP modules require this. There are various tools that can help you with WSDL generation, although one of the easiest ways is to simply start with a simple WSDL file. e.g. the file that corresponds to the MyFunction call as in your code above:

<definitions name = "MyService"
   targetNamespace = "http://www.examples.com/wsdl/MyService.wsdl"
   xmlns = "http://schemas.xmlsoap.org/wsdl/"
   xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns = "http://www.examples.com/wsdl/MyService.wsdl"
   xmlns:xsd = "http://www.w3.org/2001/XMLSchema">

   <message name = "MyFunctionRequest">
      <part name = "testParam" type = "xsd:string"/>
   </message>
   <message name = "MyFunctionResponse">
      <part name = "status" type = "xsd:string"/>
   </message>
   <portType name = "MyPort">
      <operation name = "MyFunction">
         <input message = "tns:MyFunctionRequest"/>
         <output message = "tns:MyFunctionResponse"/>
      </operation>
   </portType>

   <binding name = "MyFunction_Binding" type = "tns:MyPort">
      <soap:binding style = "rpc"
         transport = "http://schemas.xmlsoap.org/soap/http"/>
      <operation name = "MyFunction">
         <soap:operation soapAction = "MyFunction"/>
         <input>
            <soap:body encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/" namespace = "urn:examples:MyService" use = "encoded"/>
         </input>
         <output>
            <soap:body encodingStyle = "http://schemas.xmlsoap.org/soap/encoding/" namespace = "urn:examples:MyService" use = "encoded"/>
         </output>
      </operation>
   </binding>

   <service name = "MyService">
      <documentation>WSDL File for MyService</documentation>
      <port binding = "tns:MyFunction_Binding" name = "MyPort">
         <soap:address
            location = "http://www.examples.com/MyFunction/" />
      </port>
   </service>

</definitions>

You'd call using client code like this:

var soap = require('soap');
var url = 'http://localhost/wsdl?wsdl';
var args = {name: 'value'};
soap.createClient(url, function(err, client) {
  client.MyFunction(args, function(err, result) {
      console.log(result);
  });
});

Have a look at Client.describe() also, this is very useful and will return an object showing all methods the server supports.

Upvotes: 13

Related Questions