SOAP server with Express.js

13,852

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

enter image description here

or by SoapUI

enter image description here

Check posted wsdl can be retrievable by browser

enter image description here

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 */
Share:
13,852

Related videos on Youtube

newbie
Author by

newbie

Updated on September 14, 2022

Comments

  • newbie
    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
    Martin over 4 years
    I like this anwser a lot. But could you explain to me the: <soap:address location="efqf-test.prc.us6.oraclecloud.com:443/prcPoEditDoc‌​umentOrder/…> 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
    Steve Park over 4 years
    The 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 by soap.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
    Steve Park over 4 years
    I 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
    Martin over 4 years
    Thanks 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. :)