C# SOAP client: sending generic XmlNode request - c#

I have a C# project where I added a SOAP service reference, using the integrated visual studio functionality (right click -> add -> service reference)
The client classes are generated correctly without errors. However, the various methods of the service only accept a generic System.Xml.XmlNode as an input, rather than a structured object.
This should not be a problem in theory, since I have the complete XML file with the query that I need to perform. So I tried doing it like this:
NSIStdV20ServiceSoapClient client = new NSIStdV20ServiceSoapClient();
var getAllDataFlowQuery = File.ReadAllText(#"Query\get_all_dataflow.xml"); //file containing the query
XmlDocument doc = new XmlDocument();
doc.LoadXml(getAllDataFlowQuery);
var dataStructures = client.QueryStructure(doc); //this method accepts a System.Xml.XmlNode as parameter
However, this doesn't work, throwing
System.ServiceModel.FaultException: 'Error due to a non correct client message'
I thought initially that the query was incorrect, but I tried to perform the exact same query using SoapUI and it works perfectly! I even tried doing it with the exact XML returned by doc.InnerXml (just to be sure che XmlDocument object was not modifying the XML) and it works.
So basically it's only when calling the method from C# that it doesn't work.
If you want to try it out yourself, the service is freely accessible, the WSDL is here:
http://sdmx.istat.it/SDMXWS/NsiStdV20Service.asmx?WSDL
and you should try to call the QueryStructure method with the following payload:
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://ec.europa.eu/eurostat/sri/service/2.0"><soapenv:Header /><soapenv:Body><web:QueryStructure><!--Optional:--><web:Query><RegistryInterface xsi:schemaLocation="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message SDMXMessage.xsd" xmlns="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message" xmlns:common="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/common" xmlns:compact="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/compact" xmlns:cross="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/cross" xmlns:generic="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/generic" xmlns:query="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/query" xmlns:structure="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/structure" xmlns:registry="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/registry" xmlns:utility="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/utility" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Header><ID>JD014</ID><Test>true</Test><Truncated>false</Truncated><Name xml:lang="en">Trans46302</Name><Prepared>2001-03-11T09:30:47-05:00</Prepared><Sender id="BIS" /></Header><QueryStructureRequest resolveReferences="false"><registry:DataflowRef /></QueryStructureRequest></RegistryInterface></web:Query></web:QueryStructure></soapenv:Body></soapenv:Envelope>
As I said, this works perfectly in SoapUI, but doesn't work when calling the client method from C#

Well, it seems that the client generated by visual studio, even tho it accepts a XmlNode as input, creates some of the required outer structure itself (to be precise: all the outer nodes with the soapenv and web namespaces).
Which means I had to strip down the input XML to:
<?xml version="1.0" encoding="UTF-8"?><RegistryInterface xsi:schemaLocation="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message SDMXMessage.xsd" xmlns="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message" xmlns:common="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/common" xmlns:compact="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/compact" xmlns:cross="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/cross" xmlns:generic="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/generic" xmlns:query="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/query" xmlns:structure="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/structure" xmlns:registry="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/registry" xmlns:utility="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/utility" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Header><ID>JD014</ID><Test>true</Test><Truncated>false</Truncated><Name xml:lang="en">Trans46302</Name><Prepared>2001-03-11T09:30:47-05:00</Prepared><Sender id="BIS" /></Header><QueryStructureRequest resolveReferences="false"><registry:DataflowRef /></QueryStructureRequest></RegistryInterface>

Related

Why does the Receive.dll in Biztalk 2020, wrap my xml message in a string element with a xmlns?

I am using HTTP Handlers with Receive.dll on IIS. When the website sends a message for example:
<ns0:Select xmlns:ns0="http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/MainData">
<ns0:Columns>LocationID,BulletinLevel,NotifyUponCompletion,SupervisorName,CrewMemberID,AircraftID</ns0:Columns>
<ns0:Query></ns0:Query>
</ns0:Select>
it gets wrapped with a string element like this:
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
<ns0:Select xmlns:ns0="http://schemas.microsoft.com/Sql/2008/05/TableOp/dbo/MainData"><ns0:Columns>LocationID,BulletinLevel,NotifyUponCompletion,SupervisorName,CrewMemberID,AircraftID</ns0:Columns><ns0:Query></ns0:Query></ns0:Select>
</string>
When it hits the orchestration it fails to find the Select schema. I tried using a pass through on the Receive shape and remove the <string> element in a construct using an external class and re-assigning the message with the new value, but still fails. Not sure how to get around this.

How to serialize an object into string\xml with its headers

I'm using a third side web service client (created by using the "Add service reference") in order to retrieve some data.
After filling the web service objects with proper data we need to add some data to the headers (encrypted password and some other predefined data)
Then, we are serializing every request sent to the web service, using the standard .net XmlSerializer.
However, in the result of the serialization I don't see the headers of the request. I've searched for a long time and couldn't find any way to "print" them as well.
Here is some example code:
Ibooking proxy = new BookingManager();
/* Init proxy Data...*/
GetAvailabilityRequest request = new GetAvailabilityRequest();
/*Fill more data on the request...*/
GetAvailabilityResponse response = proxy.GetAvailability(request); //Send request to the web service
var xmlString2 = response.Serialize(); //only body, no headers in the XML
/* Extension class to Serialize any object */
public static class ExtensionUtil
{
public static string Serialize<T>(this T value)
{
try
{
XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var writer = XmlWriter.Create(stringWriter))
{
xmlserializer.Serialize(writer, value);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
}
I've excluded the code that adds more data to the request since it's long and complicated (need to implement IEndpointBehavior and IClientMessageInspector to "catch" the request before we send it) - but currently as a workaround I put a BreakPoint on the Message object and convert it into string using Visual Studio. In this way I do see the headers but obviously this is bad practice since I want it to be automated in the serialization.
I would like to see an example of how you are adding these headers.
In most web services the message body is the part that is serialized into XML or JSON - the headers are not.
You may be able to inspect the service call by using Fiddler and a proxy implemented by a small change in your web.config as described in this article: http://weblog.west-wind.com/posts/2008/Mar/14/Debugging-Http-or-Web-Services-Calls-from-ASPNET-with-Fiddler.
The short version of this is to add the following to your web.config or app.config:
<system.net>
<defaultProxy>
<proxy proxyaddress="http://127.0.0.1:8888" />
</defaultProxy>
</system.net>
Download and run Fiddler while calling the service and you should see and be able to inspect the call in Fiddler.
If you want to inspect and/or modify the headers within your code base could look into implementing IClientMessageInspector or IDispatchMessageInspector. Here are a couple articles on the topic:
https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector(v=vs.100).aspx
http://weblogs.asp.net/paolopia/writing-a-wcf-message-inspector
Here is an implementation I did. I didn't need access the headers, but rather to modify the xml namespaces created by the service client, but it should give you an idea on how to do the implementation: How can I create custom XML namespace attributes when consuming a legacy SOAP service?
OperationContext is your friend here. Use an OperationContextScope to wrap the call to the service, then use OperationContext.Current to get at all the hidden goodies you need.
https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontextscope(v=vs.110).aspx
Note that you'll need to know the specific types of the headers you want to get at, and I've had some trouble getting at the values, rather than just the names, of headers if they're not marked as serializable when using XmlSerializer

accessing php soapservice from C#

well i wanted to make a simple webservice that searches the db and return the data i know i can do it with mysql connector but this is just to learn how to use soaps here is the code for php soap server
require_once ('lib/nusoap.php');
$namespace = "http://localhost/webservice/index.php?wsdl";
$server = new soap_server();
$server->configureWSDL("DBQuery");
$server->wsdl->schemaTargetNamespace = $namespace;
$server->register(
'QueryMsg',
array('name'=>'xsd:string'),
array('return'=>'xsd:string'),
$namespace,
false,
'rpc',
'encoded',
'returns data from database');
function QueryMsg($query)
{
$con=mysqli_connect('localhost','root','','webserivce');
if (mysqli_connect_errno()) {
return "Failed to connect to MySQL: " . mysqli_connect_error();
}
if(!isset($query) or strpos(strtolower($query),'select')<=-1)
{
return "invalid order";
}
else
{
mysqli_real_escape_string($con,$query);
$result = mysqli_query($con,$query);
while($row = mysqli_fetch_array($result)) {
$data[] = $row;}
return json_encode($data);
}
}
// create HTTP listener
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
exit();
?>
it works when i try calling it from a php soap client but when i try adding this http:// localhost /webservice/index.php in visual studio as service refernce to consume it from C# application i get an error here it is
The HTML document does not contain Web service discovery information.
Metadata contains a reference that cannot be resolved: 'http://localhost/webservice/index.php'.
The content type text/xml; charset=ISO-8859-1 of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 700 bytes of the response were: '<?xml version="1.0" encoding="ISO-8859-1"?><SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body><SOAP-ENV:Fault><faultcode xsi:type="xsd:string">SOAP-ENV:Client</faultcode><faultactor xsi:type="xsd:string"></faultactor><faultstring xsi:type="xsd:string">Operation &apos;&apos; is not defined in the WSDL for this service</faultstring><detail xsi:type="xsd:string"></detail></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>'.
The remote server returned an error: (500) Internal Server Error.
If the service is defined in the current solution, try building the solution and adding the service reference again.
solved : well it was easy actually there is two ways either use WCF and change encoding to ISO-8859-1
or change encoding of the web service itself by adding this line $server->soap_defencoding = 'UTF-8'; after creating the soap server
I would try adding the service WSDL with a tool like SOAP U.I. and see what kind of errors you get back from that. It's a little more agnostic than adding a web reference with C#, and might disclose more details about why at the client level you can't consume this.
I'm happy to help you troubleshoot this with a little more information. Are you running this service on the same machine where you're running the client from? If it's complaining about being unable to correlate the file http://localhost/webservice/index.php to something I wonder if the discovery process is trying to evaluate a file that can't be found. I.E. an import operation in your source WSDL that points to a URL the client can't resolve.

How to handle/cleanse invalid 0x14 in XML using Linq?

I'm working on a C# client that downloads XML files from a web service via SOAP. For some of the older records hosted on the service, the XML comes across with, apparently, a 0x14 buried somewhere in it, which kicks an "Invalid whitespace character" exception. I'm using Linq to dump the XML into files. Is there some way to instruct Linq to dispose of invalid characters without losing the rest of the XML?
EDIT:
Here's the code I currently have for putting the XML to file:
XDocument c =
new XDocument(
new XElement(nameSpace + "getCitationsResponse",
new XAttribute(XNamespace.Xmlns + "ns1", nameSpace),
new XElement("list",
record.reportDateSpecified ? new XElement("reportDate", record.reportDate) : null,
new XElement("reportType", record.reportType),
new XElement("title", record.title),
new XElement("projectNumber", record.projectNumber),
new XElement("author", record.author),
new XElement("abstract", record.#abstract),
new XElement("numPages", record.numPages),
record.isDataTypeSpecified ? new XElement("isDataType", record.isRestrictedData) : null,
new XElement("comments", record.comments),
new XElement("attachments", from a in record.attachments
select new XElement("list",
new XElement("id", a.id),
new XElement("filePath", a.filePath),
new XElement("type", a.type)))));
I had to hack out some of it for the usual reasons, but what I removed is identical to the what's shown here.
I used SoapUI before I posted to see if I could figure out where the flaw was, but I don't see anything in SoapUI, and it doesn't generate an error itself.
EDIT #2:
Here's the exact error message and stack trace. Makes me wonder if I can actually do something about it or if I just need to work in something to log which records have invalid characters and try to pull 'em down manually with SoapUI.
Invalid white space character (0x14) in text to output
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at Downloader.WebService.ApiService.getRecords(String username, String[] ids)
at Downloader.Central.RecordLoop(ApiService svc, Int32 offset, String username)
getRecords is the API call generated by the wsdl, and RecordLoop is a recursive function I wrote to handle iterating through the API call to find updated records and push them to the Linq function I posted already.
As mentioned in some of the comments above, it's possible to do execute different ninja tricks to get the SOAP response to comply with the XML specification.
If you choose to change the response to make it valid XML, you have to seriously consider if your changes are changing the meaning of the response.
As I see it, the problem is not on your side, but on the service side. If you can, you should try to get the service owner to upgrade the service to deliver proper formatted XML in their web services.
When dealing with 3rd party web services, I normally do the following:
Enable full XML Schema validation on any requests and responses to and from 3rd web services. If the requests or responses aren't XML schema valid, then we (client and service) have a problem, which can be minor or major - but at least it's being attended to.
Always log any schema validation errors, before trying to fix the content, to be sure that it's on record.
Ensure that I'm fully aware of the system, business or legal implications on modifying the content.
Ensure that I'm encoding the response using the proper encoding format - UTF8, Latin1 or others.
Invalid content is typically xml text elements, that contain illegal XML characters. The service side should either use XML encoding or base64 encoding, when transferring such text nodes, both to preserve formatting as well as content.
On the more technical part of actually changing the content so it become valid, I would normally add WCF behaviours, that would address the issues as doing so, kind of separate the concerns of fixing up the xml and the business purpose of service invocation.
It's also easy to remove the WCF behaviour, if or when the service gets updated to provide valid XML at any request.

How to get XMLResponse from Webservice in c# Soap or other soap

I want to integrate with webservice writes in php (PEAR SOAP).
Wsdl file is without types definition.
When i was connect to webservice i getting a null response.
In WebServiceStudio i see xmlrequest and xmlresponse, my I get xmlresponse in c# default soap or other soap.
I know what is causing the null response. PEAR SOAP returnx STRUCT[X] as a response type, where X is number of list element.
When I my get XMLresponse and replace this section then it would be cool
Regards
Sorry for my english
The method you need is getLastResponse(), but if it's anything like other, similar HTTP-stream classes, you'll probably need to set a flag to enable capturing the raw requests/responses. Then again, maybe not, since you apparently have to use parseResposne to get the data out of the XML.
Try:
$soapClient->call(some,parameters,here);
$response = $soapClient->getLastResponse();
echo $response;

Categories