I'm making a NodeJS web service that contains API REST and SOAP methods (Im using https://www.npmjs.com/package/soap) with Express. With API REST I don't have any problem but with SOAP I have an inconvenient, when I try to consume the SOAP method from a testing C# application I can see that the parameters are going fine, but in the response I have the next error in C# (Response is not correct XML code)
When I consume the method from a NodeJS client with node-soap too the response working fine.
Part of my NodeJS code:
const express = require('express');
const bodyParser = require('body-parser');
const soap = require('soap');
const fs = require('fs');
const xml = fs.readFileSync('src/templates/ws_soap.wsdl', 'utf8');
const app = express();
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(bodyParser.json());
const soap_service = {
integrations: {
pull: {
getSnapshotGIGA: function(args) {
return {
res: "HOLA"
};
},
}
}
};
app.listen(port, ip, function() {
soap.listen(app, '/integrations_service', soap_service, xml, function() {
console.log('SOAP web service started on ' + ip + ':' + port);
});
console.log('API REST started on ' + ip + ':' + port);
});
My WSDL file is next (In response I have the type string because I wanted to see how it behaved, but I need to return an object XML):
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="integrations_service" targetNamespace="http://localhost:4205/integrations_service" xmlns="http://localhost:4205/integrations_service" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<wsdl:message name="getSnapshotGIGARequest">
<wsdl:part name="User" type="xs:string"/>
<wsdl:part name="Password" type="xs:string"/>
</wsdl:message>
<wsdl:message name="getSnapshotGIGAResponse">
<wsdl:part name="res" type="xs:string"/>
</wsdl:message>
<wsdl:portType name="pull_integrations">
<wsdl:operation name="getSnapshotGIGA">
<wsdl:input message="getSnapshotGIGARequest"/>
<wsdl:output message="getSnapshotGIGAResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="pull_integrations_binding" type="pull_integrations">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getSnapshotGIGA">
<soap:operation soapAction="getSnapshotGIGA"/>
<wsdl:input>
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="integrations">
<wsdl:port binding="pull_integrations_binding" name="pull">
<soap:address location="http://localhost:4205/integrations_service"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
In C# I have an console application and I have registered the SOAP service as a web referency.
The way I consume the SOAP method is (When I make a SOAP Service with C# I test methods from this way too because is the way clients working):
Console.WriteLine("Consume NodeJS SOAP service");
Thread.Sleep(500);
integrations_service.integrations integrations = new integrations_service.integrations();
integrations.Url = "http://localhost:4205/integrations_service?wsdl";
var some_response = integrations.getSnapshotGIGA("myuser", "123456");
Console.WriteLine("Press enter to out...");
I want to get the response in and XmlNode like in this example:
Console.WriteLine("Consume C# SOAP service");
Thread.Sleep(500);
serviceSOAP sSOAP = new serviceSOAP ();
sSOAP.Url = "http://my.domain.com.mx/";
XmlNode xmlNode = sSOAP .anyMethodSoap("yomero", "123456");
Console.WriteLine(XElement.Parse(xmlNode.OuterXml).ToString());
Thread.Sleep(500);
If you know how I can return the XML from NodeJS and get it correctly in C# or any idea, I would appreciate it. Reggards.
Answer to my question, the problem was the WSDL configuration. With this configuration I made this works with C# web reference. The principal problem, the style, I changed it from "rpc" to "document" and configure correctly the element for the response.
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
name="devices_service"
targetNamespace="http://localhost:4205/devices_service"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:s="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://localhost:4205/devices_service">
<wsdl:types>
<xs:schema targetNamespace="http://localhost:4205/devices_service" xmlns="http://localhost:4205/devices_service" attributeFormDefault="qualified" elementFormDefault="qualified">
<xs:element name="GetDevicesRequest">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="User" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" name="Password" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="GetDevicesResponse">
<xs:complexType>
<xs:sequence>
<xs:any/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:message name="GetDevicesSoapIn">
<wsdl:part name="parameters" element="GetDevicesRequest"/>
</wsdl:message>
<wsdl:message name="GetDevicesSoapOut">
<wsdl:part name="parameters" element="GetDevicesResponse"/>
</wsdl:message>
<wsdl:portType name="user_devices">
<wsdl:operation name="GetDevices">
<wsdl:input message="GetDevicesSoapIn"/>
<wsdl:output message="GetDevicesSoapOut"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="user_devices_binding" type="user_devices">
<s:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
<wsdl:operation name="GetDevices">
<s:operation soapAction="http://localhost:4205/devices_services/GetDevices"/>
<wsdl:input>
<s:body use="literal" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:input>
<wsdl:output>
<s:body use="literal" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="devices">
<wsdl:port binding="user_devices_binding" name="user">
<s:address location="http://localhost:4205/devices_service"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>**
You'll probably want to use one of:
node-soap
strong-soap (rewrite of node-soap)
easysoap
Can somebody help me access this web service? How am i going to call this service on my code? Will this work?
MyService.InputParameters input = new MyService.InputParameters();
input.attribute = " blah blah";
MyService.OutputParameteers = execute(input);
Thanks you !!!!
<wsdl:message name="requestMessage">
<wsdl:part name="request" element="inp1:InputParameters"/>
</wsdl:message>
<wsdl:message name="replyMessage">
<wsdl:part name="reply" element="inp1:OutputParameters"/>
</wsdl:message>
<wsdl:portType name="execute_ptt">
<wsdl:operation name="execute">
<wsdl:input message="tns:requestMessage"/>
<wsdl:output message="tns:replyMessage"/>
</wsdl:operation>
</wsdl:portType>
James,
I think this is what you are looking for.
http://code.msdn.microsoft.com/How-to-call-External-WCF-42c4490d
Hope this helps !!!
I have made a simple web service
wsdl:
<wsdl:definitions name='mysum' >
<wsdl:types>
<xsd:schema
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.my-uni-project.info/joomla/components/com_jv_vm_soa/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="mysum"
targetNamespace="http://www.my-uni-project.info/joomla/components/com_jv_vm_soa/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<xsd:complexType name="mysumRequest">
<xsd:all>
<xsd:element minOccurs="0" name="n1" type="xsd:int"/>
<xsd:element minOccurs="0" name="n2" type="xsd:int"/>
</xsd:all>
</xsd:complexType>
<xsd:element name="mysumResponse" type="xsd:int"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="mysumRequest">
<wsdl:part name="parameters" element="tns:mysumRequest" />
</wsdl:message>
<wsdl:message name="mysumResponse">
<wsdl:part name="result" element="tns:mysumResponse" />
</wsdl:message>
<wsdl:portType name="mysum">
<wsdl:operation name="mysum">
<wsdl:input message="tns:mysumRequest"/>
<wsdl:output message="tns:mysumResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="mysumSOAP" type="tns:mysum">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="mysum">
<soap:operation soapAction="mysum" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="mysum">
<wsdl:port name="mysumSOAP" binding="tns:mysumSOAP">
<soap:address location="http://www.my-uni-
project.info/joomla/components/com_jv_vm_soa/mysum.php" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
the service:
function mysum($parameters) {
$result = $parameters->item[0]->value + $parameters->item[1]->value;
return $result ;
}
ini_set("soap.wsdl_cache_enabled", "0"); // disabling WSDL cache
$server = new SoapServer("mysum.wsdl");
$server->addFunction("mysum");
$server->handle();
that I can access from a php client:
$client = new SoapClient("http://www.my-uni-
project.info/joomla/components/com_jv_vm_soa/mysum.wsdl");
$params = array('n1' => '4', 'n2' => '8');
try {
$result = $client->__soapCall('mysum', array('parameters' => $params));
echo $result;
} catch (SoapFault $exception) {
echo $exception;
}
I tried to create a C# client so first I created a service reference "mysum", then on the form I added a button and a label and I added the following code for the button
private void button1_Click(object sender, EventArgs e)
{
mysum s = new mysum();
label1.Text = "" + s.mysum(2, 3);
}
Whe I run it I get this error:
Error 5 The type or namespace name 'mysum' could not be found (are you
missing a using directive or an assembly reference?)
The service is online
Thank you in advanced
John
Typically you can determine if you can resolve the problem using a directive by right-clicking on the object in question, in this case mysum, and seeing if you can 'Resolve Using ' where is the name of your directive.
I think your problem is that you are adding the service as a service reference instead of web service reference.
To add a web service reference
Add service reference
Hit the Advanced button on the window
Hit Add Web Reference
Enter the service url
Also,
Make sure you have added the System.Web.Services namespace reference in your project.
Hope it helps.
I've been trying for a couple of days now to get a .NET client working fully with a Web Server provided by my Coldfusion-based web app. I'm not a .NET developer, per se, but I happen to have a copy of VS 2003, which seems like it should do the trick.
I can use a simple multiplier() method in my web service that takes two numbers and returns a number, so simple types are working fine. It's the complex types that are killing me. I'm basically trying to return an associative array from a method called get_struct(). I get an object of type Map back, but the property (called item), which is supposed to be an array with two elements (of type mapItem) always has an "undefined value".
Here's the WSDL that is generated by ColdFusion:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://trunk.v.pfapi.remote_api" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://trunk.v.pfapi.remote_api" xmlns:intf="http://trunk.v.pfapi.remote_api" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns1="http://rpc.xml.coldfusion" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--WSDL created by Macromedia ColdFusion MX version 7,0,2,142559-->
<wsdl:types>
<schema targetNamespace="http://rpc.xml.coldfusion" xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://trunk.v.pfapi.remote_api"/>
<import namespace="http://xml.apache.org/xml-soap"/>
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="CFCInvocationException">
<sequence/>
</complexType>
<complexType name="QueryBean">
<sequence>
<element name="columnList" nillable="true" type="impl:ArrayOf_xsd_string"/>
<element name="data" nillable="true" type="impl:ArrayOfArrayOf_xsd_anyType"/>
</sequence>
</complexType>
</schema>
<schema targetNamespace="http://xml.apache.org/xml-soap" xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://trunk.v.pfapi.remote_api"/>
<import namespace="http://rpc.xml.coldfusion"/>
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="mapItem">
<sequence>
<element name="key" nillable="true" type="xsd:anyType"/>
<element name="value" nillable="true" type="xsd:anyType"/>
</sequence>
</complexType>
<complexType name="Map">
<sequence>
<element maxOccurs="unbounded" minOccurs="0" name="item" type="apachesoap:mapItem"/>
</sequence>
</complexType>
</schema>
<schema targetNamespace="http://trunk.v.pfapi.remote_api" xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://rpc.xml.coldfusion"/>
<import namespace="http://xml.apache.org/xml-soap"/>
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="ArrayOf_xsd_string">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
</restriction>
</complexContent>
</complexType>
<complexType name="ArrayOfArrayOf_xsd_anyType">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:anyType[][]"/>
</restriction>
</complexContent>
</complexType>
</schema>
</wsdl:types>
<wsdl:message name="CFCInvocationException">
<wsdl:part name="fault" type="tns1:CFCInvocationException"/>
</wsdl:message>
<wsdl:message name="multiplierResponse">
<wsdl:part name="multiplierReturn" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="get_structResponse">
<wsdl:part name="get_structReturn" type="apachesoap:Map"/>
</wsdl:message>
<wsdl:message name="struct_keycountResponse">
<wsdl:part name="struct_keycountReturn" type="xsd:double"/>
</wsdl:message>
<wsdl:message name="get_structRequest">
</wsdl:message>
<wsdl:message name="multiplierRequest">
<wsdl:part name="factor1" type="xsd:double"/>
<wsdl:part name="factor2" type="xsd:double"/> </wsdl:message>
<wsdl:message name="struct_keycountRequest">
<wsdl:part name="theStruct" type="apachesoap:Map"/>
</wsdl:message>
<wsdl:portType name="remote_io_test">
<wsdl:operation name="multiplier" parameterOrder="factor1 factor2">
<wsdl:input message="impl:multiplierRequest" name="multiplierRequest"/>
<wsdl:output message="impl:multiplierResponse" name="multiplierResponse"/>
<wsdl:fault message="impl:CFCInvocationException" name="CFCInvocationException"/>
</wsdl:operation>
<wsdl:operation name="get_struct">
<wsdl:input message="impl:get_structRequest" name="get_structRequest"/>
<wsdl:output message="impl:get_structResponse" name="get_structResponse"/>
<wsdl:fault message="impl:CFCInvocationException" name="CFCInvocationException"/>
</wsdl:operation>
<wsdl:operation name="struct_keycount" parameterOrder="theStruct">
<wsdl:input message="impl:struct_keycountRequest" name="struct_keycountRequest"/>
<wsdl:output message="impl:struct_keycountResponse" name="struct_keycountResponse"/>
<wsdl:fault message="impl:CFCInvocationException" name="CFCInvocationException"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="remote_io_test.cfcSoapBinding" type="impl:remote_io_test">
<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="multiplier">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="multiplierRequest">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:input>
<wsdl:output name="multiplierResponse">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:output>
<wsdl:fault name="CFCInvocationException">
<wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="CFCInvocationException" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="get_struct">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="get_structRequest">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:input>
<wsdl:output name="get_structResponse">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:output>
<wsdl:fault name="CFCInvocationException">
<wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="CFCInvocationException" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="struct_keycount">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="struct_keycountRequest">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:input>
<wsdl:output name="struct_keycountResponse">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:output>
<wsdl:fault name="CFCInvocationException">
<wsdlsoap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" name="CFCInvocationException" namespace="http://trunk.v.pfapi.remote_api" use="encoded"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="remote_io_testService">
<wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
This is a collection of test methods to allow remote developers
to evaluate datatype support, etc in their programming environment.
The WSDL endpoint for this web service is [YOUR PEERFOCUS SITE]/remote_api/pfapi/v/trunk/remote_io_test.cfc?wsdl </wsdl:documentation>
<wsdl:port binding="impl:remote_io_test.cfcSoapBinding" name="remote_io_test.cfc">
<wsdlsoap:address location="http://leon.cupahr.tafkan.localhost/remote_api/pfapi/v/trunk/remote_io_test.cfc"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
and here's the web service stub generated when I add a web reference in VS:
//------------------------------------------------------------------------------
// <autogenerated>
// This code was generated by a tool.
// Runtime Version: 1.1.4322.2443
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------
//
// This source code was auto-generated by Microsoft.VSDesigner, Version 1.1.4322.2443.
//
namespace pfapi_test.remote_io_test {
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;
/// <remarks/>
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="remote_io_test.cfcSoapBinding", Namespace="http://trunk.v.pfapi.remote_api")]
[System.Xml.Serialization.SoapIncludeAttribute(typeof(QueryBean))]
[System.Xml.Serialization.SoapIncludeAttribute(typeof(CFCInvocationException))]
public class remote_io_testService : System.Web.Services.Protocols.SoapHttpClientProtocol {
/// <remarks/>
public remote_io_testService() {
this.Url = "http://leon.cupahr.tafkan.nooch/remote_api/pfapi/v/trunk/remote_io_test.cfc";
}
/// <remarks/>
[System.Web.Services.Protocols.SoapRpcMethodAttribute("", RequestNamespace="http://trunk.v.pfapi.remote_api", ResponseNamespace="http://trunk.v.pfapi.remote_api")]
[return: System.Xml.Serialization.SoapElementAttribute("multiplierReturn")]
public System.Double multiplier(System.Double factor1, System.Double factor2) {
object[] results = this.Invoke("multiplier", new object[] {
factor1,
factor2});
return ((System.Double)(results[0]));
}
/// <remarks/>
public System.IAsyncResult Beginmultiplier(System.Double factor1, System.Double factor2, System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("multiplier", new object[] {
factor1,
factor2}, callback, asyncState);
}
/// <remarks/>
public System.Double Endmultiplier(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((System.Double)(results[0]));
}
/// <remarks/>
[System.Web.Services.Protocols.SoapRpcMethodAttribute("", RequestNamespace="http://trunk.v.pfapi.remote_api", ResponseNamespace="http://trunk.v.pfapi.remote_api")]
[return: System.Xml.Serialization.SoapElementAttribute("get_structReturn")]
public Map get_struct() {
object[] results = this.Invoke("get_struct", new object[0]);
return ((Map)(results[0]));
}
/// <remarks/>
public System.IAsyncResult Beginget_struct(System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("get_struct", new object[0], callback, asyncState);
}
/// <remarks/>
public Map Endget_struct(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((Map)(results[0]));
}
/// <remarks/>
[System.Web.Services.Protocols.SoapRpcMethodAttribute("", RequestNamespace="http://trunk.v.pfapi.remote_api", ResponseNamespace="http://trunk.v.pfapi.remote_api")]
[return: System.Xml.Serialization.SoapElementAttribute("struct_keycountReturn")]
public System.Double struct_keycount(Map theStruct) {
object[] results = this.Invoke("struct_keycount", new object[] {
theStruct});
return ((System.Double)(results[0]));
}
/// <remarks/>
public System.IAsyncResult Beginstruct_keycount(Map theStruct, System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("struct_keycount", new object[] {
theStruct}, callback, asyncState);
}
/// <remarks/>
public System.Double Endstruct_keycount(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((System.Double)(results[0]));
}
}
/// <remarks/>
[System.Xml.Serialization.SoapTypeAttribute("Map", "http://xml.apache.org/xml-soap")]
public class Map {
/// <remarks/>
public mapItem[] item;
}
/// <remarks/>
[System.Xml.Serialization.SoapTypeAttribute("mapItem", "http://xml.apache.org/xml-soap")]
public class mapItem {
/// <remarks/>
public object key;
/// <remarks/>
public object value;
}
/// <remarks/>
[System.Xml.Serialization.SoapTypeAttribute("QueryBean", "http://rpc.xml.coldfusion")]
public class QueryBean {
/// <remarks/>
public string[] columnList;
/// <remarks/>
public object[] data;
}
/// <remarks/>
[System.Xml.Serialization.SoapTypeAttribute("CFCInvocationException", "http://rpc.xml.coldfusion")]
public class CFCInvocationException {
}
}
And finally, my CLI app that tests the service:
using System;
namespace pfapi_test
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Console.WriteLine("Instantiating WS");
remote_io_test.remote_io_testService testWS = new remote_io_test.remote_io_testService();
Console.WriteLine("Calling multiplier(3,15)");
Console.WriteLine(testWS.multiplier(3,15));
Console.WriteLine("Calling get_struct()");
remote_io_test.Map theStruct = testWS.get_struct();
Console.Write("result: ");
Console.WriteLine(theStruct);
Console.Write("result.item: ");
Console.WriteLine(theStruct.item);
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}
No matter what I try, theStruct.item is always "undefined value" according to the debugger. The printed output looks like:
Instantiating WS
Calling multiplier(3,15)
45
Calling get_struct()
result: pfapi_test.remote_io_test.Map
result.item:
Press Enter to exit...
I've tried using ColdFusion 8, and there's no difference. I've tried returning a real custom database with two properties instead of my ad-hoc associative array, and that works fine, but rewriting my API to avoid associative arrays isn't really an option at this time. The API works fine with ColdFusion, PHP/NuSOAP, and Ruby on Rails, so it seems like it should be possible to get it working with .NET as well.
I hope someone can provide some insight. I have the suspicion that there's some kind of namespace issue here, but I don't know SOAP and XML well enough to figure out what it is. I've also searched like mad for a solution online, but I haven't found a single person who's solved this problem, which is very disheartening!
Request:
POST /remote_api/pfapi/v/trunk/remote_io_test.cfc HTTP/1.1
VsDebuggerCausalityData: [snip]
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 1.1.4322.2443)
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Content-Length: 488
Expect: 100-continue
Host: leon.cupahr.tafkan.nooch
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://trunk.v.pfapi.remote_api" xmlns:types="http://trunk.v.pfapi.remote_api/encodedTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:get_struct />
</soap:Body>
</soap:Envelope>
Response:
HTTP/1.1 200 OK
Date: Thu, 17 Dec 2009 15:14:33 GMT
Server: Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.7l DAV/2 PHP/5.2.8 JRun/4.0 Phusion_Passenger/2.2.7
Set-Cookie: CFID=21543;expires=Sat, 10-Dec-2039 15:14:33 GMT;path=/
Set-Cookie: CFTOKEN=479cc311ca4875db-9D346355-ED36-6183-C8895635E4EE1252;expires=Sat, 10-Dec-2039 15:14:33 GMT;path=/
Transfer-Encoding: chunked
Content-Type: text/xml; charset=utf-8
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:get_structResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://trunk.v.pfapi.remote_api">
<get_structReturn xsi:type="ns2:Map" xmlns:ns2="http://xml.apache.org/xml-soap">
<item xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<key xsi:type="soapenc:string">FOO</key>
<value xsi:type="soapenc:string">bar</value>
</item>
<item>
<key xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">ANOTHERKEY</key>
<value xsi:type="soapenc:string" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">another value</value>
</item>
</get_structReturn>
</ns1:get_structResponse>
</soapenv:Body>
</soapenv:Envelope>
Update: I heard from the .NET developer on the other end, and he's tried all of the following to no avail:
using WCF instead of ASMX
using .NET 3.5
using VB.NET to communicate with the web service instead of C#
He did send me a screenshot of the "Data Type" section of the Service Reference Settings dialog. Is it possible that changing the settings for Collection type and Dictionary collection type might resolve this issue?
Update 2: here's the ColdFusion code for remote_io_test.cfc
<cfcomponent name="remote_io_test"
hint="This is a collection of test methods to allow remote developers
to evaluate datatype support, etc in their programming environment.">
<cffunction name="get_struct" returntype="struct" access="remote" output="no"
hint="Returns an associative array with two keys, 'foo' and 'anotherKey'.
Allows you to test your implementation's support for WDDX encoding.
ColdFusion and PHP (w/ NuSOAP) will automatically decode the result
into an associative array. Feedback on other languages is appreciated.">
<cfset var stFoo = structNew()>
<cfset stFoo.foo = "bar">
<cfset stFoo.anotherKey = "another value">
<cfreturn duplicate(stFoo)>
</cffunction> <!--- get_struct --->
<cffunction name="multiplier" returntype="numeric" access="remote" output="no"
hint="Multiplies two factors and returns the result. Allows you to test
passing simple variables to a remote method.">
<cfargument name="factor1" type="numeric" required="yes">
<cfargument name="factor2" type="numeric" required="yes">
<cfreturn factor1 * factor2>
</cffunction> <!--- multiplier --->
<cffunction name="struct_keycount" returntype="numeric" access="remote" output="no"
hint="Returns the number of keys in an upload associative array. Allows
you to test passing complex variables to a remote method.">
<cfargument name="theStruct" type="struct" required="yes">
<cfreturn structCount(theStruct)>
</cffunction> <!--- struct_keycount --->
Thanks for reading, and thanks in advance for your replies!
Cross-posted at link text
I've been struggling with this item myself from the .NET client side of things - I'm trying to consume a web service written in ColdFusion. From the documentation I found, the struct data type in ColdFusion does not map directly onto any Web Services types, and it would seem to me that it is a fault with Axis.
You've probably read the same web pages as I have when trying to solve this problem, but it would seem if you want to create a web service using ColdFusion, the recommendation is not to use the struct type. If your web service is not live yet, and you can get away with using something different, I would suggest going down this route. I tried returning objects from ColdFusion, which worked fine for me from a .NET client.
I actually gave up on this problem and wrote a custom client for my .NET program which would just read the Xml returned by the ColdFusion web service and convert this into a dictionary, as I have no influence over the service I am trying to call.
One thing I did find during my testing - the service I am trying to call is running on an older version of ColdFusion (7 I believe). Whenever I call that service, the .NET client returns null. I installed ColdFusion on my own machine and wrote a simple service, and found that the .NET client returned an object of type Map (which had come from the WSDL), but the properties of the Map object were all null.
I'd be interested to see if Chris Haas's suggestion in the comments to your question of setting dotNetSoapEncFix solves the problem.
The answer is: don't use Coldfusion structs in your web service.
Like everyone else before us, we gave up and rewrote our API to not use any associative arrays in the request or response. We're now using scalars, arrays, and complexTypes that are automatically created from CFCs. This time around, we tested our proof-of-concept with PHP, Ruby, Coldfusion, Java, and .NET to make sure it was actually interoperable.
It makes a lot of sense now that a statically-typed language would be unable to handle a totally arbitrary remote datatype in any sensible way.
Thanks for all of your feedback!
There is a solution to your problem and it is here:
http://ws-i.org/Profiles/BasicProfile-2.0-2010-11-09.html#soapenc_Array
http://ws-i.org/Profiles/BasicProfile-1.2-2010-11-09.html#soapenc_Array
Your problem lays here to be exact:
<complexType name="ArrayOf_xsd_string">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
</restriction>
</complexContent>
</complexType>
<complexType name="ArrayOfArrayOf_xsd_anyType">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:anyType[][]"/>
</restriction>
</complexContent>
</complexType>
Just redefine those types like this:
<element name="ArrayOf_xsd_string" type="tns:OnlyStringArrayType"/>
<complexType name="OnlyStringArrayType">
<sequence>
<element name="array_element" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="ArrayOfArrayOf_xsd_anyType" type="tns:AnyTypeArrayType"/>
<complexType name="AnyTypeArrayType">
<sequence>
<element name="array_element" type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
it still doesn't fully conform to the WS-I profiles mentioned above , because of the element names, but it should be very easy to process by the clients and the class file generators.
I wrote this solution for others who would stumble upon this problem :)
And by the way WSDL and SOAP it is a one big pain in the.... ;)
Background:
I'm in the process of creating a web service using ASP.NET 2.0. This web service provides another interface to an existing web form which contains selection boxes dynamically populated from a database.
My first draft of the web service accepted a string for each of these and then ensured that it was valid, throwing back an error if it wasn't. However the consumer of the web service has asked, since the possible values aren't likely to change all that often, that we provide enumerated values in the WSDL.
I am reluctant to create an enumeration with my web service code, so I have instead altered the generated WSDL file and instructed my web service to use that instead of inspecting my classes to generate it.
WSDL:
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://example.org/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://example.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://example.org/">
<s:element name="MyMethod">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="myClass" type="tns:MyClass" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="MyClass">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="MyString" type="tns:MyStringPossibleValues" />
</s:sequence>
</s:complexType>
<s:element name="MyMethodResponse">
<s:complexType />
</s:element>
<s:simpleType name="MyStringPossibleValues">
<s:restriction base="s:string">
<s:enumeration value="alpha" />
<s:enumeration value="bravo" />
</s:restriction>
</s:simpleType>
</s:schema>
</wsdl:types>
<wsdl:message name="MyMethodSoapIn">
<wsdl:part name="parameters" element="tns:MyMethod" />
</wsdl:message>
<wsdl:message name="MyMethodSoapOut">
<wsdl:part name="parameters" element="tns:MyMethodResponse" />
</wsdl:message>
<wsdl:portType name="ExternalAccessSoap">
<wsdl:operation name="MyMethod">
<wsdl:input message="tns:MyMethodSoapIn" />
<wsdl:output message="tns:MyMethodSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:portType name="ExternalAccessHttpGet" />
<wsdl:portType name="ExternalAccessHttpPost" />
<wsdl:binding name="ExternalAccessSoap" type="tns:ExternalAccessSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="MyMethod">
<soap:operation soapAction="http://example.org/MyMethod" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="ExternalAccessSoap12" type="tns:ExternalAccessSoap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="MyMethod">
<soap12:operation soapAction="http://example.org/MyMethod" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
</wsdl:definitions>
Webservice:
namespace Example.Service
{
[WebService(Namespace = "http://example.org/")]
[WebServiceBinding(
ConformsTo = WsiProfiles.BasicProfile1_1,
Location="ExternalAccess.wsdl",
Name="ExternalAccessSoap",
Namespace = "http://example.org/")]
[ToolboxItem(false)]
public class ExternalAccess : System.Web.Services.WebService
{
public class MyClass
{
public string MyString;
}
[WebMethod]
[SoapDocumentMethod(
Action = "http://example.org/MyMethod",
RequestNamespace = "http://example.org/",
Binding="ExternalAccessSoap")]
public void MyMethod(MyClass myClass)
{
}
}
}
The problem:
As the WSDL specifies an enumeration for MyString and the code specified a string type, ASP.NET does not manage to map the fields correctly.
Is there an attribute I can use to instruct the deserialiser to populate the string field with the value of the enumeration?
Regards,
Matt
Having gone through the process of creating a soap extension to do this for me I discovered that MyString wasn't actually being sent to my web service.
This was because the test application for this service was built in .NET also and, when building the request object, the MyStringSpecified property of the generated proxy class was overlooked. This then prevented the enumerated value being sent as part of the SOAP request.
When this property was set to true, the enumerated value was successfully assigned to the MyString field in the webservice.