Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

SOAP driver for Node.js (A complete rewrite of node-soap)

License

NotificationsYou must be signed in to change notification settings

loopbackio/strong-soap

LoopBack4 logo

CICoverage Status

TwitterLinkedInSlack

This module provides a Node.js SOAP client for invoking web services and a mock-up SOAP server capability to create and test your web service. This module is based onnode-soap module.

Overview

Features:

  • Full SOAP Client capability and mock-up SOAP server capability
  • Handles both RPC and Document styles
  • Handles both SOAP 1.1 and SOAP 1.2 Fault
  • APIs to parse XML into JSON and JSON into XML
  • API to describe WSDL document
  • Support for both synchronous and asynchronous method handlers
  • WS-Security (currently only UsernameToken and PasswordText encoding is supported)

Install

Install withnpm:

npm install strong-soap

Client

Start with the WSDL for the web service you want to invoke. For example, the stock quote servicehttp://www.webservicex.net/stockquote.asmx and the WSDL ishttp://www.webservicex.net/stockquote.asmx?WSDL

Create a new SOAP client from WSDL URL usingsoap.createClient(url[, options], callback). Also supports a local file system path. An instance ofClient is passed to thesoap.createClient callback. It is used to execute methods on the soap service.

"use strict";varsoap=require('strong-soap').soap;// wsdl of the web service this client is going to invoke. For local wsdl you can use, url = './wsdls/stockquote.wsdl'varurl='http://www.webservicex.net/stockquote.asmx?WSDL';varrequestArgs={symbol:'IBM'};varoptions={};soap.createClient(url,options,function(err,client){varmethod=client['StockQuote']['StockQuoteSoap']['GetQuote'];method(requestArgs,function(err,result,envelope,soapHeader){//response envelopeconsole.log('Response Envelope: \n'+envelope);//'result' is the response bodyconsole.log('Result: \n'+JSON.stringify(result));});});

As well as creating a client via aurl, an existingWSDL object can be passed in viaoptions.WSDL_CACHE.

varsoap=require('strong-soap').soap;varWSDL=soap.WSDL;varurl='http://www.webservicex.net/stockquote.asmx?WSDL';// Pass in WSDL options if anyvaroptions={};WSDL.open(url,options,function(err,wsdl){// You should be able to get to any information of this WSDL from this object. Traverse// the WSDL tree to get  bindings, operations, services, portTypes, messages,// parts, and XSD elements/Attributes.// Set the wsdl object in the cache. The key (e.g. 'stockquotewsdl')// can be anything, but needs to match the parameter passed into soap.createClient()varclientOptions={WSDL_CACHE :{stockquotewsdl:wsdl}};soap.createClient('stockquotewsdl',clientOptions,function(err,client){varmethod=client['StockQuote']['StockQuoteSoap']['GetQuote'];method(requestArgs,function(err,result,envelope,soapHeader){//response envelopeconsole.log('Response Envelope: \n'+envelope);//'result' is the response bodyconsole.log('Result: \n'+JSON.stringify(result));});});});

The Request envelope created by above service invocation:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">  <soap:Header/>  <soap:Body>    <ns1:GetQuotexmlns:ns1="http://www.webserviceX.NET/">      <ns1:symbol>IBM</ns1:symbol>    </ns1:GetQuote>  </soap:Body></soap:Envelope>

This WSDL operation is defined as document/literal-wrapped style. Hence the request in soap is wrapped in operation name. Refer to test casesserver-client-document-test andserver-client-rpc-test to understand document and rpc styles and theirRequest, Response and Fault samples.

Theoptions argument allows you to customize the client with the following properties:

  • endpoint: to override the SOAP service's host specified in the.wsdl file.
  • request: to override therequest module.
  • httpClient: to provide your own http client that implementsrequest(rurl, data, callback, exheaders, exoptions).
  • envelopeKey: to set specific key instead of
    <soap:Body></soap:Body>
  • wsdl_options: custom options for the request module on WSDL requests.
  • wsdl_headers: custom HTTP headers to be sent on WSDL requests.

Note: for versions of node >0.10.X, you may need to specify{connection: 'keep-alive'} in SOAP headers to avoid truncation of longer chunked responses.

Extra headers (optional)

User can define extra HTTP headers to be sent on the request.

varclientOptions={};soap.createClient(url,clientOptions,function(err,client){varcustomRequestHeader={customheader1:'test1'};// Custom request headerclient.GetQuote(requestArgs,function(err,result,envelope){// Result in SOAP envelope body which is the wrapper element.// In this case, result object corresponds to GetCityForecastByZIPResponse.console.log(JSON.stringify(result));},null,customRequestHeader);});

Client.describe()

Describes services, ports and methods as a JavaScript object.

// Describes the entire WSDL in a JSON tree object form.vardescription=client.describe();// Inspect GetQuote operation. You can inspect Service: {Port: {operation: {console.log(JSON.stringify(description.StockQuote.StockQuoteSoap.GetQuote));

Client.setSecurity(security)

Use the specified security protocol.

Refer to test casessl-test for an example of using this API.

Client.method(args, callback)

Callmethod on the SOAP service.

client.MyFunction({name:'value'},function(err,result,envelope,soapHeader){// Result is a javascript object// Envelope is the response envelope from the Web Service// soapHeader is the response soap header as a JavaScript object})

Amethod can also be called as a promise.

client.MyFunction({name:'value'}).then(function({result, envelope, soapHeader}){// ...},function(err){// ...});// in async/await flavortry{const{result, envelope, soapHeader}=awaitclient.MyFunction({name:'value'});}catch(err){// handle error}

Client.service.port.method(args, callback[, options[, extraHeaders]])

Call amethod using a specificservice andport.

client.MyService.MyPort.MyFunction({name:'value'},function(err,result){// Result is a JavaScript object})

Options (optional)

Accepts any option that the request module accepts, seerequest module.

For example, you could set a timeout of 5 seconds on the request like this:

client.MyService.MyPort.MyFunction({name:'value'},function(err,result){// result is a javascript object},{timeout:5000})

You can measure the elapsed time on the request by passing the time option:

client.MyService.MyPort.MyFunction({name:'value'},function(err,result){// client.lastElapsedTime - the elapsed time of the last request in milliseconds},{time:true})

Alternative method call using callback-last pattern

To align method call signature with Node's standard callback-last pattern and eventually allow promisification of method calls, the following method signatures are also supported:

client.MyService.MyPort.MyFunction({name:'value'},options,function(err,result){// result is a javascript object})client.MyService.MyPort.MyFunction({name:'value'},options,extraHeaders,function(err,result){// result is a javascript object})

Client.lastRequest

The property that contains last full soap request for client logging.

Client.setEndpoint(url)

Overwrites the SOAP service endpoint address.

Client events

Client instances emit the following events:

  • request - Emitted before a request is sent. The event handler receives theentire Soap request (Envelope) including headers.
  • message - Emitted before a request is sent. The event handler receives theSoap body contents. Useful if you don't want to log /store Soap headers.
  • soapError - Emitted when an erroneous response is received.Useful if you want to globally log errors.
  • response - Emitted after a response is received. The event handler receivesthe SOAP response body as well as the entireIncomingMessage response object.This is emitted for all responses (both success and errors).

For an example of using this API, seessl-test.

Here is an example of 'soapError' event

soap.createClient(__dirname+'/wsdl/default_namespace.wsdl',function(err,client){vardidEmitEvent=false;client.on('soapError',function(err){didEmitEvent=true;assert.ok(err.root.Envelope.Body.Fault);});client.MyOperation({},function(err,result){assert.ok(didEmitEvent);done();});},baseUrl);

Security

strong-soap has several default security protocols. You can easily add your ownas well. The interface is quite simple. Each protocol defines two methods:

  • addOptions - Method that accepts an options arg that is eventually passed directly torequest
  • toXML - Method that returns a string of XML.

BasicAuthSecurity

client.setSecurity(newsoap.BasicAuthSecurity('username','password'));

BearerSecurity

client.setSecurity(newsoap.BearerSecurity('token'));

ClientSSLSecurity

Note: If you run into issues using this protocol, consider passing these optionsas default request options to the constructor:

  • rejectUnauthorized: false
  • strictSSL: false
  • secureOptions: constants.SSL_OP_NO_TLSv1_2 (this is likely needed for node >= 10.0)
client.setSecurity(newsoap.ClientSSLSecurity('/path/to/key','/path/to/cert',{/*default request options*/}));

WSSecurity

WSSecurity implements WS-Security. UsernameToken and PasswordText/PasswordDigest is supported.

varwsSecurity=newWSSecurity(username,password,options)//the 'options' object is optional and contains properties://passwordType: 'PasswordDigest' or 'PasswordText' default is PasswordText//hasTimeStamp: true or false, default is true//hasTokenCreated: true or false, default is trueclient.setSecurity(wsSecurity);

WSSecurityCert

WS-Security X509 Certificate support.

varprivateKey=fs.readFileSync(privateKeyPath);varpublicKey=fs.readFileSync(publicKeyPath);varpassword='';// optional passwordvarwsSecurity=newsoap.WSSecurityCert(privateKey,publicKey,password,'utf8');client.setSecurity(wsSecurity);

Note: Optional dependency 'ursa' is required to be installed successfully when WSSecurityCert is used.

ClientSSLSecurityPFX

constpfxSecurity=newsoap.ClientSSLSecurityPFX(pathToPfxOrFileBuffer,passphrase)client.setSecurity(pfxSecurity)

XML attributes

Handling XML attributes, value, and XML (wsdlOptions)

To override the default behavior ofstrong-soap, use thewsdlOptions object, passed in thecreateClient() method. ThewsdlOptions has the following properties:

varwsdlOptions={attributesKey:'theAttrs',valueKey:'theVal',xmlKey:'theXml'}

If you callcreateClient() with no options (or an empty Object{}),strong-soap defaultsto the following:

  • attributesKey :'$attributes'
  • valueKey :'$value'
  • xmlKey :'$xml'

Overriding the value key

By default,strong-soap uses$value as key for any parsed XML value which may interfere with your other code as itcould be some reserved word, or the$ in general cannot be used for a key to start with.

You can define your ownvalueKey by passing it in thewsdl_options to the createClient call like so:

varwsdlOptions={valueKey:'theVal'};soap.createClient(__dirname+'/wsdl/default_namespace.wsdl',wsdlOptions,function(err,client){// your code});

Overriding the xml key

AsvalueKey,strong-soap uses$xml as key. The xml key is used to pass XML Object without adding namespace or parsing the string.

Example :

dom={$xml:'<parentnode type="type"><childnode></childnode></parentnode>'};
<tns:dom>    <parentnodetype="type">          <childnode></childnode>    </parentnode></tns:dom>

You can define your ownxmlKey by passing it in thewsdl_options to the createClient call like this:

varwsdlOptions={xmlKey:'theXml'};soap.createClient(__dirname+'/wsdl/default_namespace.wsdl',wsdlOptions,function(err,client){// your code});

Overriding the attributes key

You can achieve attributes like:

<parentnode>  <childnodename="childsname">  </childnode></parentnode>

By attaching an attributes object to a node.

{parentnode:{childnode:{$attributes:{name:'childsname'}}}}

However, "attributes" may be a reserved key for some systems that actually want a node:

<attributes></attributes>

In this case you can configure the attributes key in thewsdlOptions like this:

varwsdlOptions={attributesKey:'$attributes'};

Adding xsiType

soap.createClient(__dirname+'/wsdl/default_namespace.wsdl',wsdlOptions,function(err,client){client.*method*({parentnode:{childnode:{$attributes:{$xsiType:"{xmlnsTy}Ty"}}}});});

Removing the xsiType. The resulting Request shouldn't have the attribute xsiType

soap.createClient(__dirname+'/wsdl/default_namespace.wsdl',wsdlOptions,function(err,client){client.*method*({parentnode:{childnode:{$attributes:{}}}});});

To see it in practice, consider the sample in:test/request-response-samples/addPets__force_namespaces

XMLHandler

XMLHandler enables you to to convert a JSON object to XML and XML to a JSON object. It can also parse an XML string or stream into the XMLBuilder tree.

API to convert JSON object to XML and XML to JSON object:

varsoap=require('..').soap;varXMLHandler=soap.XMLHandler;varxmlHandler=newXMLHandler();varutil=require('util');varurl='http://www.webservicex.net/stockquote.asmx?WSDL';varrequestArgs={symbol:'IBM'};varoptions={};varclientOptions={};soap.createClient(url,clientOptions,function(err,client){varcustomRequestHeader={customheader1:'test1'};client.GetQuote(requestArgs,function(err,result,envelope,soapHeader){// Convert 'result' JSON object to XMLvarnode=xmlHandler.jsonToXml(null,null,XMLHandler.createSOAPEnvelopeDescriptor('soap'),result);varxml=node.end({pretty:true});console.log(xml);// Convert XML to JSON objectvarroot=xmlHandler.xmlToJson(null,xml,null);console.log('%s',util.inspect(root,{depth:null}));},options,customRequestHeader);});

Parse XML string or stream into the XMLBuilder tree:

varroot=XMLHandler.parseXml(null,xmlString);

WSDL

wsdl.open(wsdlURL, options, callback(err, wsdl))

Loads WSDL into a tree form. Traverse through WSDL tree to get to bindings, services, ports, operations, and so on.

Parameters:

  • wsdlURL WSDL url to load.
  • options WSDL options
  • callback Error and WSDL loaded into object tree.
varsoap=require('..').soap;varWSDL=soap.WSDL;varpath=require('path');// Pass in WSDL options if anyvaroptions={};WSDL.open('./wsdls/stockquote.wsdl',options,function(err,wsdl){// You should be able to get to any information of this WSDL from this object. Traverse// the WSDL tree to get  bindings, operations, services, portTypes, messages,// parts, and XSD elements/Attributes.vargetQuoteOp=wsdl.definitions.bindings.StockQuoteSoap.operations.GetQuote;// print operation nameconsole.log(getQuoteOp.$name);varservice=wsdl.definitions.services['StockQuote'];//print service nameconsole.log(service.$name);});

wsdl.openSync(wsdlURL, options)

Loads WSDL into a tree form directly from memory. It traverses through WSDL tree to get to bindings, services, ports, operations, and so on as long as you have your dependent WSDLs and schemas are loaded and available in theoptions.WSDL_CACHE. If any I/O is required to retrieve any dependencies this call will throw an error.

Parameters:

  • wsdlURL WSDL url to load as named in the cache.
  • options WSDL options

An example of loading WSDLs into youroptions.WSDL_CACHE and callingwsdl.loadSync() can be found in the testtest/wsdl-load-from-memory-test

Server

soap.listen(server,path,services,wsdl)

Creates a new SOAP server that listens onpath and providesservices.

wsdl is an xml string that defines the service.

varmyService={MyService:{MyPort:{MyFunction:function(args){return{name:args.name};},// This is how to define an asynchronous function.MyAsyncFunction:function(args,callback){// do some workcallback({name:args.name});},// This is how to receive incoming headersHeadersAwareFunction: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};}}}};varxml=require('fs').readFileSync('myservice.wsdl','utf8'),server=http.createServer(function(request,response){response.end("404: Not Found: "+request.url);});server.listen(8000);soap.listen(server,'/wsdl',myService,xml);

An example of using the SOAP server is intest/server-client-document-test

Options

You can pass in server andWSDL Optionsusing an options hash.

varxml=require('fs').readFileSync('myservice.wsdl','utf8');soap.listen(server,{// Server options.path:'/wsdl',services:myService,xml:xml,// WSDL options.attributesKey:'theAttrs',valueKey:'theVal',xmlKey:'theXml'});

Server logging

If thelog method is defined it will be called with 'received' and 'replied'along with data.

server=soap.listen(...)server.log=function(type,data){// type is 'received' or 'replied'};

Server events

Server instances emit the following events:

  • request - Emitted for every received messages.The signature of the callback isfunction(request, methodName).
  • headers - Emitted when the SOAP Headers are not empty.The signature of the callback isfunction(headers, methodName).

The sequence order of the calls isrequest,headers and then the dedicatedservice method.

test.soapServer.on('request',functionrequestManager(request,methodName){assert.equal(methodName,'GetLastTradePrice');done();});

An example of using the SOAP server is intest/server-test

SOAP Fault

A service method can reply with a SOAP Fault to a client bythrowing anobject with aFault property.

Example SOAP 1.1 Fault:

test.service={DocLiteralWrappedService:{DocLiteralWrappedPort:{myMethod:function(args,cb,soapHeader){throw{Fault:{faultcode:"sampleFaultCode",faultstring:"sampleFaultString",detail:{myMethodFault:{errorMessage:'MyMethod Business Exception message',value:10}}}}}}}}

SOAP 1.2 Fault:

test.service={DocLiteralWrappedService:{DocLiteralWrappedPort:{myMethod:function(args,cb,soapHeader){throw{Fault:{Code:{Value:"soap:Sender",Subcode:{Value:"rpc:BadArguments"}},Reason:{Text:"Processing Error"},Detail:{myMethodFault2:{errorMessage2:'MyMethod Business Exception message',value2:10}}}}}}}}

Examples of SOAP 1.1/SOAP 1.2 Fault response can be found in testtest/server-client-document-test

Server security example using PasswordDigest

Ifserver.authenticate is not defined then no authentication will take place.

server=soap.listen(...)server.authenticate=function(security){varcreated,nonce,password,user,token;token=security.UsernameToken,user=token.Username,password=token.Password,nonce=token.Nonce,created=token.Created;returnuser==='user'&&password===soap.passwordDigest(nonce,created,'password');};

Server connection authorization

Theserver.authorizeConnection method is called prior to the soap service method.If the method is defined and returnsfalse then the incoming connection isterminated.

server=soap.listen(...)server.authorizeConnection=function(req){returntrue;// or false};

SOAP headers

Received SOAP headers

A service method can look at the SOAP headers by providing a third arguments.

{HeadersAwareFunction:function(args,cb,headers){return{name:headers.Token};}}

It is also possible to subscribe to the 'headers' event.The event is triggered before the service method is called, and only when theSOAP Headers are not empty.

server=soap.listen(...)server.on('headers',function(headers,methodName){// It is possible to change the value of the headers// before they are handed to the service method.// It is also possible to throw a SOAP Fault});

First parameter is the Headers object;second parameter is the name of the SOAP method that will called(in case you need to handle the headers differently based on the method).

Outgoing SOAP headers

Both client and server can define SOAP headers that will be added to what they send.They provide the following methods to manage the headers.

addSoapHeader(value, qname)

Adds soapHeader to soap:Header node.

Parameters:

  • value JSON object representing {headerName: headerValue} or XML string.
  • qname qname used for the header
addSoapHeader(value, qname, options);

Returns the index where the header is inserted.

changeSoapHeader(index, value, qname)

Changes an existing soapHeader.

Parameters:

  • index index of the header to replace with provided new value
  • value JSON object representing {headerName: headerValue} or XML string.
  • qname qname used for the header

getSoapHeaders()

Returns all defined headers.

clearSoapHeaders()

Removes all defined headers.

Examples of using SOAP header API are in:test/server-test andtest/server-test

soap-stub

Unit testing services that use SOAP clients can be very cumbersome. To getaround this you can usesoap-stub in conjunction withsinon to stub soap withyour clients.

Example

varsinon=require('sinon');varsoapStub=require('strong-soap/soap-stub');varurlMyApplicationWillUseWithCreateClient='./example/stockquote.wsdl';varclientStub={SomeOperation:sinon.stub()};clientStub.SomeOperation.respondWithError=soapStub.createRespondingStub({error:'error'});clientStub.SomeOperation.respondWithSuccess=soapStub.createRespondingStub({success:'success'});// or if you are using promisesclientStub.SomeOperation.respondWithError=soapStub.createRespondingStubAsync({error:'error'});clientStub.SomeOperation.respondWithSuccess=soapStub.createRespondingStubAsync({success:'success'});soapStub.registerClient('my client alias',urlMyApplicationWillUseWithCreateClient,clientStub);varfs=require('fs'),assert=require('assert'),request=require('@cypress/request'),http=require('http'),lastReqAddress;describe('myService',function(){varclientStub;varmyService;beforeEach(function(){clientStub=soapStub.getStub('my client alias');soapStub.reset();myService=clientStub;});describe('failures',function(){beforeEach(function(){clientStub.SomeOperation.respondWithError();});it('should handle error responses',function(){myService.SomeOperation(function(err,response){// handle the error response.});});});});

Contributors

About

SOAP driver for Node.js (A complete rewrite of node-soap)

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors175


[8]ページ先頭

©2009-2025 Movatter.jp