Introduction
Node.js is becoming a great choice for developers in start ups and enterprises. While REST web services are the preferred choice for new node development, there is also a need to call existing web services from within Node apps. When calling WCF web services this is a challenge since WCF supports a lot of standards and protocols which are not easy to generate. Wcf.js is a library for node.js that aims to simplify this scenario.
Background
The first question that you may be asking is why do we need a special library at all for calling SOAP web services. After all HTTP is a first class citizen in node.js and we can simply send the SOAP over HTTP to the service. However web services may support standards like Ws-Security or MTOM which node does not support out of the box. Actually this is exactly what another project, ws.js, solves.
So if we have ws.js why do we also need wcf.js?
The two projects are complementing each other. While wcf.js does not add any new standards implementations on top of ws.js, it mimics the WCF API. This means users can access WCF using its native API and do not need to learn a new paradigm. And all this is done in a pure JavaScript way!
Using the code
1. First you must install the Wcf.js module:
npm install wcf.js
2. Now write your code:
var BasicHttpBinding = require('wcf.js').BasicHttpBinding
, Proxy = require('wcf.js').Proxy
, binding = new BasicHttpBinding(
{ SecurityMode: "TransportWithMessageCredential"
, MessageClientCredentialType: "UserName"
})
, proxy = new Proxy(binding, "http://localhost:7171/Service/clearUsername")
, message = "<Envelope xmlns='http://schemas.xmlsoap.org/soap/envelope/'>" +
"<Header />" +
"<Body>" +
"<GetData xmlns='http://tempuri.org/'>" +
"<value>123</value>" +
"</GetData>" +
"</Body>" +
"</Envelope>"
proxy.ClientCredentials.Username.Username = "yaron"
proxy.ClientCredentials.Username.Password = "1234"
proxy.send(message, "http://tempuri.org/IService/GetData", function(response, ctx) {
console.log(response)
});
If this code looks familiar then you are right - it is the same as the WCF API. So you can freely create bindings and behaviors the same way you would do that with WCF. The above example initializes a wcf basicHttpBinding in this line:
binding = new BasicHttpBinding(
{ SecurityMode: "TransportWithMessageCredential"
, MessageClientCredentialType: "UserName"
})
Then it configures credentails:
proxy.ClientCredentials.Username.Username = "yaron"
proxy.ClientCredentials.Username.Password = "1234"
and then calls the service:
proxy.send(message, "http://tempuri.org/IService/GetData", function(response, ctx) {
...
});
Exactly as with WCF!
Custom binding sample
The same approach can help us build a custom binding:
var CustomBinding = require('wcf.js').CustomBinding
, MtomMessageEncodingBindingElement = require('wcf.js').MtomMessageEncodingBindingElement
, HttpTransportBindingElement = require('wcf.js').HttpTransportBindingElement
, Proxy = require('wcf.js').Proxy
, fs = require('fs')
, binding = new CustomBinding(
[ new MtomMessageEncodingBindingElement({MessageVersion: "Soap12WSAddressing10"}),
, new HttpTransportBindingElement()
])
, proxy = new Proxy(binding, "http://localhost:7171/Service/mtom")
, message = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
'<s:Header />' +
'<s:Body>' +
'<EchoFiles xmlns="http://tempuri.org/">' +
'<value xmlns:a="http://schemas.datacontract.org/2004/07/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">' +
'<a:File1 />' +
'</value>' +
'</EchoFiles>' +
'</s:Body>' +
'</s:Envelope>'
proxy.addAttachment("//*[local-name(.)='File1']", "me.jpg");
proxy.send(message, "http://tempuri.org/IService/EchoFiles", function(response, ctx) {
var file = proxy.getAttachment("//*[local-name(.)='File1']")
fs.writeFileSync("result.jpg", file)
});
What you should note here is how channels are defined on the custom binding:
binding = new CustomBinding(
[ new MtomMessageEncodingBindingElement({MessageVersion: "Soap12WSAddressing10"}),
, new HttpTransportBindingElement()
])
You can create your own channels and put them in the pipeline. This specific example uses the mtom channel.
Why MTOM requires extra work?
The above example uses the MTOM channel. This is why we are required to do some more work by specifying the base64 element path:
proxy.addAttachment("//*[local-name(.)='File1']", "me.jpg");
We are not required to do this in WCF so why does Wcf.js requires this?
Let's take a look at this soap element:
<a:File1 />
If MTOM was not used then this section was filled with the base64 representation of me.jpg:
<a:File1>PS2Kqw23A2A98...</a:File1>
We need to tell the MTOM channel to take this content and put it outside the message as a binary payload. So why WCF does not require us to tell it this information? WCF is a full soap stack which also generates proxies based on WSDL. When WCF generated the File1 parameter it marked it as a candidate for MTOM transformation. So Wcf has this information from the Wsdl. Since Wcf.js is a pure ws-* stack (and not soap stack), it does not have access to the wsdl and we need to supply this information.
Supported Bindings
Wcf.js currently supports a subset of:
- BasicHttpBinding
- WSHttpBinding
- CustomBinding
The current subset includes:
- MTOM / Text encodings
- WS-Addressing (all versions)
- Transport Security (SSL)
- Transport with message credential (Username)
The future of Ws.js
Wcf.js is a pure JavaScript implementation of WCF for Node.js.
Wcf.js is a growing framework. For future versions I plan to add more binding support like net.tcp and proprietary security negotiations in WsHttpBinding. If you have any special request send me an email from my blog. If you want to help feel free to fork Wcf.js on github - that's the fastest way to grow Wcf.js.
More information
Check out the project github page
Check out my blog
Check out my twitter
Drop me an email from my blog