111

I wonder what is the best way to consume SOAP XML web service with node.js

Thanks!

WHITECOLOR
  • 24,996
  • 37
  • 121
  • 181
  • In case you use node-soap and figured out how to use it, could you help me with creating a wsdl. Is there a generator or a good tutorial how to write the wsdl. http://stackoverflow.com/questions/32480481/creating-a-soap-webservice-with-node-soap – Andi Giga Sep 10 '15 at 06:02
  • In case you need an example for .NET WCF service call, check my answer https://stackoverflow.com/a/63351804/1370029 – Aliaksei Maniuk Aug 11 '20 at 05:07

16 Answers16

91

You don't have that many options.

You'll probably want to use one of:

Juicy Scripter
  • 25,778
  • 6
  • 72
  • 93
  • 3
    Thanks. having problems with node-soap install because node-expat installation failure =( – WHITECOLOR Dec 28 '11 at 16:44
  • You'll need expat development headers to build it – Juicy Scripter Dec 28 '11 at 17:52
  • I found the issue its been said about headers, but I don't know where should I get it where should I put it to compile, could you explain, please? – WHITECOLOR Dec 28 '11 at 17:54
  • 1
    Probably you can get 'em via package management tools for your OS. On Ubuntu for example `sudo apt-get install libexpat1-dev` – Juicy Scripter Dec 28 '11 at 18:28
  • i found strong-soap to be useful (based on node-soap). strong-soap addressed some of my issues with consuming a web service that had some null parameters – Robert Broden Nov 01 '16 at 19:17
  • 1
    @RobertBroden, thanks for the update. Please next time go ahead and edit the answer (or suggest an edit)! – Juicy Scripter Nov 02 '16 at 13:05
  • So far, I had a lot of trouble with node-soap. It generates requests that I found to be invalid when pasted into Soap UI. Manually creating the requests (answer below) may sometime be a viable solution too. – Eric Burel Dec 28 '16 at 15:43
33

I think that an alternative would be to:

Yes, this is a rather dirty and low level approach but it should work without problems

tmanolatos
  • 932
  • 10
  • 16
  • 5
    Sadly, this is the most reliable method for interacting with SOAP with Node.js. I've yet to find a single soap library that properly makes soap requests on the handful of API's I have to use. – AlbertEngelB Dec 11 '14 at 20:22
  • 1
    100% dirty, but brought me to results))) – Mark Okhman Sep 01 '15 at 20:08
  • what do you all mean with to form input xml` exactly ? – timaschew Jan 24 '16 at 15:29
  • yeah, can confirm still, non of above mentioned libs works perfect. – someUser Nov 16 '16 at 16:19
  • I think "Form input xml" means just giving a Content-Type of "text/xml" – SSH This Aug 04 '17 at 16:13
  • Thanks for this idea, i gave up on `node-soap` and did what you suggested: https://stackoverflow.com/a/45929815/1628984 – jtlindsey Aug 29 '17 at 02:28
  • I couldn't do any correct requests with `node-soap` and with psx certificates. I'm really sure I did everything correct. Finally with this approach everything works really good. Now I'm using the libs `https` and `node-xml2js`. – Frank Roth Nov 14 '18 at 08:00
28

If node-soap doesn't work for you, just use node request module and then convert the xml to json if needed.

My request wasn't working with node-soap and there is no support for that module beyond the paid support, which was beyond my resources. So i did the following:

  1. downloaded SoapUI on my Linux machine.
  2. copied the WSDL xml to a local file
    curl http://192.168.0.28:10005/MainService/WindowsService?wsdl > wsdl_file.xml
  3. In SoapUI I went to File > New Soap project and uploaded my wsdl_file.xml.
  4. In the navigator i expanded one of the services and right clicked the request and clicked on Show Request Editor.

From there I could send a request and make sure it worked and I could also use the Raw or HTML data to help me build an external request.

Raw from SoapUI for my request

POST http://192.168.0.28:10005/MainService/WindowsService HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "http://Main.Service/AUserService/GetUsers"
Content-Length: 303
Host: 192.168.0.28:10005
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

XML from SoapUI

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:qtre="http://Main.Service">
   <soapenv:Header/>
   <soapenv:Body>
      <qtre:GetUsers>
         <qtre:sSearchText></qtre:sSearchText>
      </qtre:GetUsers>
   </soapenv:Body>
</soapenv:Envelope> 

I used the above to build the following node request:

var request = require('request');
let xml =
`<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:qtre="http://Main.Service">
   <soapenv:Header/>
   <soapenv:Body>
      <qtre:GetUsers>
         <qtre:sSearchText></qtre:sSearchText>
      </qtre:GetUsers>
   </soapenv:Body>
</soapenv:Envelope>`

var options = {
  url: 'http://192.168.0.28:10005/MainService/WindowsService?wsdl',
  method: 'POST',
  body: xml,
  headers: {
    'Content-Type':'text/xml;charset=utf-8',
    'Accept-Encoding': 'gzip,deflate',
    'Content-Length':xml.length,
    'SOAPAction':"http://Main.Service/AUserService/GetUsers"
  }
};

let callback = (error, response, body) => {
  if (!error && response.statusCode == 200) {
    console.log('Raw result', body);
    var xml2js = require('xml2js');
    var parser = new xml2js.Parser({explicitArray: false, trim: true});
    parser.parseString(body, (err, result) => {
      console.log('JSON result', result);
    });
  };
  console.log('E', response.statusCode, response.statusMessage);  
};
request(options, callback);
jtlindsey
  • 4,346
  • 4
  • 45
  • 73
  • thanks @jtlindsey. But i am getting 405 method not allowed as response.statusCode, response.statusMessage. By any chance do you know how to fix this? – Sujoy Oct 29 '18 at 18:42
  • There was issue with my URL. I was using the original URL instead of the endpoint generated by SOAPUI. Thanks for the above code. – Sujoy Oct 29 '18 at 18:52
24

I managed to use soap,wsdl and Node.js You need to install soap with npm install soap

Create a node server called server.js that will define soap service to be consumed by a remote client. This soap service computes Body Mass Index based on weight(kg) and height(m).

const soap = require('soap');
const express = require('express');
const app = express();
/**
 * this is remote service defined in this file, that can be accessed by clients, who will supply args
 * response is returned to the calling client
 * our service calculates bmi by dividing weight in kilograms by square of height in metres
 */
const service = {
  BMI_Service: {
    BMI_Port: {
      calculateBMI(args) {
        //console.log(Date().getFullYear())
        const year = new Date().getFullYear();
        const n = args.weight / (args.height * args.height);
        console.log(n);
        return { bmi: n };
      }
    }
  }
};
// xml data is extracted from wsdl file created
const xml = require('fs').readFileSync('./bmicalculator.wsdl', 'utf8');
//create an express server and pass it to a soap server
const server = app.listen(3030, function() {
  const host = '127.0.0.1';
  const port = server.address().port;
});
soap.listen(server, '/bmicalculator', service, xml);

Next, create a client.js file that will consume soap service defined by server.js. This file will provide arguments for the soap service and call the url with SOAP's service ports and endpoints.

const express = require('express');
const soap = require('soap');
const url = 'http://localhost:3030/bmicalculator?wsdl';
const args = { weight: 65.7, height: 1.63 };
soap.createClient(url, function(err, client) {
  if (err) console.error(err);
  else {
    client.calculateBMI(args, function(err, response) {
      if (err) console.error(err);
      else {
        console.log(response);
        res.send(response);
      }
    });
  }
});

Your wsdl file is an xml based protocol for data exchange that defines how to access a remote web service. Call your wsdl file bmicalculator.wsdl

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

  <message name="getBMIRequest">
    <part name="weight" type="xsd:float"/>
    <part name="height" type="xsd:float"/>
  </message>

  <message name="getBMIResponse">
    <part name="bmi" type="xsd:float"/>
  </message>

  <portType name="Hello_PortType">
    <operation name="calculateBMI">
      <input message="tns:getBMIRequest"/>
      <output message="tns:getBMIResponse"/>
    </operation>
  </portType>

  <binding name="Hello_Binding" type="tns:Hello_PortType">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="calculateBMI">
      <soap:operation soapAction="calculateBMI"/>
      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:examples:helloservice" use="encoded"/>
      </input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:examples:helloservice" use="encoded"/>
      </output>
    </operation>
  </binding>

  <service name="BMI_Service">
    <documentation>WSDL File for HelloService</documentation>
    <port binding="tns:Hello_Binding" name="BMI_Port">
      <soap:address location="http://localhost:3030/bmicalculator/" />
    </port>
  </service>
</definitions>

Hope it helps

Lin Du
  • 88,126
  • 95
  • 281
  • 483
Kim .J
  • 471
  • 5
  • 6
  • 1
    Thank you so much. However, I had to remove "res.send(response);" from the client and "`" at the last line of the server file. – Subhashi Apr 16 '18 at 13:37
15

The simplest way I found to just send raw XML to a SOAP service using Node.js is to use the Node.js http implementation. It looks like this.

var http = require('http');
var http_options = {
  hostname: 'localhost',
  port: 80,
  path: '/LocationOfSOAPServer/',
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': xml.length
  }
}

var req = http.request(http_options, (res) => {
  console.log(`STATUS: ${res.statusCode}`);
  console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
  res.setEncoding('utf8');
  res.on('data', (chunk) => {
    console.log(`BODY: ${chunk}`);
  });

  res.on('end', () => {
    console.log('No more data in response.')
  })
});

req.on('error', (e) => {
  console.log(`problem with request: ${e.message}`);
});

// write data to request body
req.write(xml); // xml would have been set somewhere to a complete xml document in the form of a string
req.end();

You would have defined the xml variable as the raw xml in the form of a string.

But if you just want to interact with a SOAP service via Node.js and make regular SOAP calls, as opposed to sending raw xml, use one of the Node.js libraries. I like node-soap.

Halfstop
  • 1,710
  • 17
  • 34
13

Depending on the number of endpoints you need it may be easier to do it manually.

I have tried 10 libraries "soap nodejs" I finally do it manually.

Theophilus Omoregbee
  • 2,463
  • 1
  • 22
  • 33
dam1
  • 3,536
  • 3
  • 22
  • 18
  • I tried node-soap for accessing wsdl route but it doesnt work , i keep getting error although the same thing works in php Can you answer my question about how you did it http://stackoverflow.com/questions/39943122/php-soap-client-equivalent-in-nodejs – Ammar Ajmal Oct 10 '16 at 15:17
9

I successfully used "soap" package (https://www.npmjs.com/package/soap) on more than 10 tracking WebApis (Tradetracker, Bbelboon, Affilinet, Webgains, ...).

Problems usually come from the fact that programmers does not investigate to much about what remote API needs in order to connect or authenticate.

For instance PHP resends cookies from HTTP headers automatically, but when using 'node' package, it have to be explicitly set (for instance by 'soap-cookie' package)...

smentek
  • 2,820
  • 1
  • 28
  • 32
6

You may also look at the easysoap npm - https://www.npmjs.org/package/easysoap -or-

https://www.npmjs.com/package/express-soap2json

wmitchell
  • 5,665
  • 10
  • 37
  • 62
euroblaze
  • 306
  • 3
  • 6
6

I used the node net module to open a socket to the webservice.

/* on Login request */
socket.on('login', function(credentials /* {username} {password} */){   
    if( !_this.netConnected ){
        _this.net.connect(8081, '127.0.0.1', function() {
            logger.gps('('+socket.id + ') '+credentials.username+' connected to: 127.0.0.1:8081');
            _this.netConnected = true;
            _this.username = credentials.username;
            _this.password = credentials.password;
            _this.m_RequestId = 1;
            /* make SOAP Login request */
            soapGps('', _this, 'login', credentials.username);              
        });         
    } else {
        /* make SOAP Login request */
        _this.m_RequestId = _this.m_RequestId +1;
        soapGps('', _this, 'login', credentials.username);          
    }
});

Send soap requests

/* SOAP request func */
module.exports = function soapGps(xmlResponse, client, header, data) {
    /* send Login request */
    if(header == 'login'){
        var SOAP_Headers =  "POST /soap/gps/login HTTP/1.1\r\nHost: soap.example.com\r\nUser-Agent: SOAP-client/SecurityCenter3.0\r\n" +
                            "Content-Type: application/soap+xml; charset=\"utf-8\"";        
        var SOAP_Envelope=  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                            "<env:Envelope xmlns:env=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:SOAP-ENC=\"http://www.w3.org/2003/05/soap-encoding\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:n=\"http://www.example.com\"><env:Header><n:Request>" +
                            "Login" +
                            "</n:Request></env:Header><env:Body>" +
                            "<n:RequestLogin xmlns:n=\"http://www.example.com.com/gps/soap\">" +
                            "<n:Name>"+data+"</n:Name>" +
                            "<n:OrgID>0</n:OrgID>" +                                        
                            "<n:LoginEntityType>admin</n:LoginEntityType>" +
                            "<n:AuthType>simple</n:AuthType>" +
                            "</n:RequestLogin></env:Body></env:Envelope>";

        client.net.write(SOAP_Headers + "\r\nContent-Length:" + SOAP_Envelope.length.toString() + "\r\n\r\n");
        client.net.write(SOAP_Envelope);
        return;
    }

Parse soap response, i used module - xml2js

var parser = new xml2js.Parser({
    normalize: true,
    trim: true,
    explicitArray: false
});
//client.net.setEncoding('utf8');

client.net.on('data', function(response) {
    parser.parseString(response);
});

parser.addListener('end', function( xmlResponse ) {
    var response = xmlResponse['env:Envelope']['env:Header']['n:Response']._;
    /* handle Login response */
    if (response == 'Login'){
        /* make SOAP LoginContinue request */
        soapGps(xmlResponse, client, '');
    }
    /* handle LoginContinue response */
    if (response == 'LoginContinue') {
        if(xmlResponse['env:Envelope']['env:Body']['n:ResponseLoginContinue']['n:ErrCode'] == "ok") {           
            var nTimeMsecServer = xmlResponse['env:Envelope']['env:Body']['n:ResponseLoginContinue']['n:CurrentTime'];
            var nTimeMsecOur = new Date().getTime();
        } else {
            /* Unsuccessful login */
            io.to(client.id).emit('Error', "invalid login");
            client.net.destroy();
        }
    }
});

Hope it helps someone

Vince Lowe
  • 3,521
  • 7
  • 36
  • 63
1

Adding to Kim .J's solution: you can add preserveWhitespace=true in order to avoid a Whitespace error. Like this:

soap.CreateClient(url,preserveWhitespace=true,function(...){
LW001
  • 2,452
  • 6
  • 27
  • 36
J.Aliaga
  • 11
  • 1
1

You can use wsdlrdr also. EasySoap is basically rewrite of wsdlrdr with some extra methods. Be careful that easysoap doesn't have the getNamespace method which is available at wsdlrdr.

Kankan-0
  • 463
  • 3
  • 11
1

For those who are new to SOAP and want a quick explanation and guide, I strongly recommend this awesome article.

You can also use node-soap package, with this simple tutorial.

MajidJafari
  • 1,076
  • 11
  • 15
1

If you just need a one-time conversion, https://www.apimatic.io/dashboard?modal=transform lets you do this by making a free account (no affiliation, it just worked for me).

If you transform into Swagger 2.0, you can make a js lib with

$ wget https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.20/swagger-codegen-cli-3.0.20.jar \
  -O swagger-codegen-cli.jar
$ java -jar swagger-codegen-cli.jar generate \
  -l javascript -i orig.wsdl-Swagger20.json -o ./fromswagger
unhammer
  • 4,306
  • 2
  • 39
  • 52
1

I had a webservice to consume with prefix - namespace and never was able to make it work with node-soap.

So i tried axios post method.

Go to your browser and paste the url information from axios: https://somewebservice.company.com.br/WCF/Soap/calc.svc?wsdl

Scroll down untill you see Soap operation name you are interested in.

soap operation name

Then copy the operation soapAction = "http://xyzxyzxyz/xyz/xyz/ObtenerSaldoDeParcelaDeEmprestimo"

in the axiosCall header.

const axiosCall = require('axios')
const xml2js = require('xml2js')

let xml = `<soapenv:Envelope 
                xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
                xmlns:tem="http://pempuri.org/" 
                xmlns:ser="http://schemas.example.org/2004/07/MyServices.Model">
            
            <soapenv:Header/>
                <soapenv:Body>
                
                <tem:DocumentState>
                <tem:DocumentData>
                     <ser:ID>0658</ser:ID>
                     <ser:Info>0000000001</ser:Info>
               </tem:DocumentData>
               </tem:DocumentState>
                
                </soapenv:Body>
            </soapenv:Envelope>         

let url = 'https://somewebservice.company.com.br/WCF/Soap/calc.svc?wsdl'

axiosCall.post( url,
            xml,
            {
                headers: {
                    'Content-Type': 'text/xml',
                    SOAPAction: 'http://xyzxyzxyz/xyz/xyz/ObtenerSaldoDeParcelaDeEmprestimo'
                }
            })
            .then((response)=>{
                 // xml2js to parse the xml response from the server 
                 // to a json object and then be able to iterate over it.

                 xml2js.parseString(response.data, (err, result) => {
                    
                    if(err) {
                        throw err;
                    }
                    console.log(result)
                }
                                        
            })

                
            })
            .catch((error)=>{
                
                console.log(error)
                
            })

Domingo
  • 165
  • 1
  • 4
0

In my opinion, avoid querying SOAP APIs with nodejs.

Two alternatives :

  1. If you're the owner of the SOAP API, make it handle both xml and json requests because javascript handles well json.

  2. Implement an API gateway in php (because php handles well SOAP). The gateway will receive your input as json, then query the SOAP API in xml and transforms the xml response into json.

mbesson
  • 629
  • 5
  • 24
0

this works like a charm for me

easy-soap-request

https://www.npmjs.com/package/easy-soap-request

simple and straightforward

Mr.P
  • 1,182
  • 3
  • 20
  • 44