ChatGPT解决这个技术问题 Extra ChatGPT

Node.js: how to consume SOAP XML web service

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

Thanks!

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. stackoverflow.com/questions/32480481/…
In case you need an example for .NET WCF service call, check my answer stackoverflow.com/a/63351804/1370029

J
Juicy Scripter

You don't have that many options.

You'll probably want to use one of:

node-soap

strong-soap (rewrite of node-soap)

easysoap


Thanks. having problems with node-soap install because node-expat installation failure =(
You'll need expat development headers to build it
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?
Probably you can get 'em via package management tools for your OS. On Ubuntu for example sudo apt-get install libexpat1-dev
@RobertBroden, thanks for the update. Please next time go ahead and edit the answer (or suggest an edit)!
t
tmanolatos

I think that an alternative would be to:

use a tool such as SoapUI (http://www.soapui.org) to record input and output xml messages

use node request (https://github.com/mikeal/request) to form input xml message to send (POST) the request to the web service (note that standard javascript templating mechanisms such as ejs (http://embeddedjs.com/) or mustache (https://github.com/janl/mustache.js) could help you here) and finally

use an XML parser to deserialize response data to JavaScript objects

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


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.
100% dirty, but brought me to results)))
what do you all mean with to form input xml` exactly ?
yeah, can confirm still, non of above mentioned libs works perfect.
I think "Form input xml" means just giving a Content-Type of "text/xml"
j
jtlindsey

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:

downloaded SoapUI on my Linux machine. copied the WSDL xml to a local file curl http://192.168.0.28:10005/MainService/WindowsService?wsdl > wsdl_file.xml In SoapUI I went to File > New Soap project and uploaded my wsdl_file.xml. 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);

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?
There was issue with my URL. I was using the original URL instead of the endpoint generated by SOAPUI. Thanks for the above code.
s
slideshowp2

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


Thank you so much. However, I had to remove "res.send(response);" from the client and "`" at the last line of the server file.
H
Halfstop

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 , could you please tell me how to make POST request using node-soap ?
@Abhisheksaini the above example is a post.
@Halfstop Kindly tell me how to include SOAPAction in the request.
T
Theophilus Omoregbee

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.

use node request (https://github.com/mikeal/request) to form input xml message to send (POST) the request to the web service

use xml2j ( https://github.com/Leonidas-from-XIV/node-xml2js ) to parse the reponse


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 stackoverflow.com/questions/39943122/…
s
smentek

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)...


using soap-cookie helped me bypass an authentication problem I was having in node, thanks a lot!
V
Vince Lowe

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


why would you do this instead of using the http module?
w
wmitchell
L
LW001

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(...){

K
Kankan-0

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.


u
unhammer

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

D
Domingo

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.

https://i.stack.imgur.com/QEQ4g.png

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)
                
            })


Thank you very much, I was stuck with node-soap. With this options in axios the request worked.
M
MajidJafari

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.


The link provided is not longer exist.
@JohnMelodyMelissaI just fixed it, thank you.
No problem pal.
m
mbesson

In my opinion, avoid querying SOAP APIs with nodejs.

Two alternatives :

If you're the owner of the SOAP API, make it handle both xml and json requests because javascript handles well json. 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.


M
Mr.P

this works like a charm for me

easy-soap-request

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

simple and straightforward