SOAP server with Express.js
Solution 1
From the pull#872 it supports express
server out of the box.
expressServer = express();
server = expressServer.listen(51515, function(){
var soapServer = soap.listen(expressServer, '/SayHello', service, wsdl);
});
where \sayHello
is the route on which you want to handle wsdl
requests.
Also, note that it will support all the middleware and body parsers as well.
For more details on syntax you can see the tests included in this pull request.
Solution 2
I think prasun gave good example already, but want to share mine with more detail. I used express and soap, but use http / https for creating server instead of using express
First, below is project structure and I modularized routes folder for RESTful endpoint routers and services folder for Soap WebService endpoints
/config/index.js
/routes/index.js
/services/wsdl/poservice.wsdl
/services/poservice.js
app.js
server.js
create router by express and build endpoint for GET request for root ('/') context path
[ routers/index.js ]
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({"data": "some data"}));
});
module.exports = router;
Build main routing map on app.js
[ app.js ]
var express = require('express');
var bodyParser = require('body-parser');
var indexRouter = require('./routes/index');
var app = express();
app.use(bodyParser.raw({type: function(){return true;}, limit: '5mb'}));
app.use('/', indexRouter);
module.exports = app;
Set service, port and operation object and also publish wsdl from filepath (./services/wsdl/PurchaseOrderServiceV2.wsdl)
[ services/poservice.js ]
var fs = require('fs');
var path = require('path');
var poServiceXml = fs.readFileSync(path.join(path.join(__dirname, 'wsdl'), 'PurchaseOrderServiceV2.wsdl'), 'utf8');
var poService = {
PurchaseOrderService: {
PurchaseOrderServiceSoapHttpPort: {
getPurchaseOrder: function(args) {
console.log(args);
return {
name: args.name
};
}
}
}
};
module.exports = {
service: poService,
xml: poServiceXml
};
This service, port and operation setup is based on the wsdl. Below is snipped wsdl service, port and operation definition
[ services/wsdl/PurchaseOrderServiceV2.wsdl ]
<wsdl:operation name="getPurchaseOrder">
<wsdl:input message="tns:PurchaseOrderService_getPurchaseOrder"/>
<wsdl:output message="tns:PurchaseOrderService_getPurchaseOrderResponse"/>
<wsdl:fault name="ServiceException" message="errors:ServiceException"/>
</wsdl:operation>
:
<wsdl:service name="PurchaseOrderService">
<wsdl:port name="PurchaseOrderServiceSoapHttpPort" binding="tns:PurchaseOrderServiceSoapHttp">
<soap:address location="https://efqf-test.prc.us6.oraclecloud.com:443/prcPoEditDocumentOrder/PurchaseOrderServiceV2"/>
:
Now create server and run RESTful and Soap endpoints
[ server.js ]
var fs = require('fs');
var config = require('./config');
var app = require('./app');
var debug = require('debug')('po-service:server');
var http = require('http');
var https = require('https');
var soap = require('soap');
const poService = require('./services/poservice');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || config.get('app.port'));
app.set('port', port);
/**
* Create HTTP server or HTTPS server
*/
var server = http.createServer(app);
if(config.get('app.https')) {
server = https.createServer({
key: fs.readFileSync(config.get('app.serverkey')),
cert: fs.readFileSync(config.get('app.servercert'))
}, app);
}
/**
* Listen on provided port, on all network interfaces.
*/
function startServer() {
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
soap.listen(server, '/prcPoEditDocumentOrder/PurchaseOrderServiceV2', poService.service, poService.xml);
}
if(!module.parent) {
// Start server if file is run directly
startServer();
} else {
// Export server, if file is referenced via cluster
module.exports = startServer;
}
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
For configuration, I have config module
[ config/index.js ]
var PropertiesReader = require('properties-reader');
var config = new PropertiesReader('app.properties');
module.exports = config;
and below is configuration file
[ app.properties ]
[app]
hostname = localhost
port = 8080
https = false
serverkey = /path/to/signed.key
servercert = /path/to/signed.crt
Now verify RESTful endpoint
$ curl http://localhost:8080/
{"data":"some data"}
Verify Soap endpoint by Advanced REST client
or by SoapUI
Check posted wsdl can be retrievable by browser
Solution 3
Code your express application as usual
var soap = require('soap');
var express = require('express');
var app = express();
app.use('/', express.static(__dirname + '/public'));
app.use('/node_modules', express.static(__dirname + '/node_modules'));
/* other express part */
but don't do app.listen
Build your server like this
var server = require('http').createServer(app);
Code soap part and terminate like this
soap.listen(server, '/wsdl', MyService, xml);
server.listen(8080);
If you want also want websocket use server too
var io = require('socket.io')(server);
/* io part */
Related videos on Youtube
newbie
Updated on September 14, 2022Comments
-
newbie over 1 year
I have created a simple SOAP server using node-soap which is currently serving the requests from a SOAP client. Now the requirement is to serve both incoming REST and SOAP requests from different clients in future. My question is can I achieve this by using a single Express app(Using the Express.js framework)? I am new to this, so any help would be appreciated. Thanks in advance!
-
Martin over 4 yearsI like this anwser a lot. But could you explain to me the: <soap:address location="efqf-test.prc.us6.oraclecloud.com:443/prcPoEditDocumentOrder/…> Tag. Looking at soap documentation it say "The SOAP 1.2 address extensibility element specifies the URL address used to connect to the SOAP server." What should the URL lead to in this example? The error i am getting is Error: Failed to bind to WSDL
-
Steve Park over 4 yearsThe PurchaseOrderServiceV2 API I used as an example is Oracle Cloud ERP API and the original snipped WSDL (not a complete WSDL I'm sharing) is showing the test environment. But the above example is the case where loading complete WSDL from local file path as
poService.xml
and setup local endpoint bysoap.listen(server, '/prcPoEditDocumentOrder/PurchaseOrderServiceV2'...
, so it doesn't need to connect to the original WSDL published URL nor original service endpoint as long as having complete WSDL in your file path. -
Steve Park over 4 yearsI tried to give an idea how to set up, but if you really want to make above code work in your environment, you can download full PurchaseOrderServiceV2.wsdl from efqf-test.prc.us6.oraclecloud.com/fscmService/… or share your project so I can help
-
Martin over 4 yearsThanks for the info. I have now manged to get it working. imporing of typs from .xsd files still problematic for me. But having it in the same file it works fine. :)