Reputation: 331
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
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
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