I have to call a webservice from withing a C# programm. The webservice has most probably not a standard format. The interface description (wsdl and xsd) are very complicated, and using a proxy generating mechanismus results in hundreds of classes. The generated classes ar of little help since they are very generic, having mostly simple Object types as members.The best option is to build the SOAP message manually. That is also the way the webservice provider suggested to chose: Take the soap/xml messages that has to be sent and build the message according to the template. Now the question is how to build the message most efficiently. Of course hard coding the message string is an option, however I wonder if better options exists. If I have the complete message in a string, how do I best send the messages. Should I use a simple HttpRequest or can I use mechanisms of the wcf stack?
My current approach to build the message looks like this:
string msg = envelopeBegin;
RouteType rootType = new RouteType();
XmlSerializer serializer = new XmlSerializer(typeof(RouteType));
StringWriter stringWriter = new StringWriter();
serializer.Serialize(stringWriter, rootType , customNamespace);
msg += stringWriter.ToString();
msg += envelopeEnd;
// Send the message over the wire
The Soap/xml message I have to generate looks like this
<env:Envelope>xmlns: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:ns0="http://www.skanska.se/oagis/9/ws/faults">
<env:Body>
<ska:ShowSalesOrder xmlns:ska="http://www.skanska.se/oagis/9" systemEnvironmentCode="UTV" versionID="1.0" releaseID="9.0">
<!--plsql=.74s-->
<ApplicationArea xmlns="http://www.openapplications.org/oagis/9">
<!--user_name=SEBA_RAPPE-->
<ska:Sender>
<LogicalID>OEBS_SE</LogicalID>
<ComponentID>SKAIS017I</ComponentID>
<AuthorizationID>SEBA_RAPPE</AuthorizationID>
<ska:ResponsibilityID>XXOM_INTEGRATION_SVT</ska:ResponsibilityID>
</ska:Sender>
<CreationDateTime>2010-02-26T15:03:27+01:00</CreationDateTime>
<BODID>xxxxxxxxxxxxxxxxx</BODID>
</ApplicationArea>
<ska:DataArea>
<Show xmlns="http://www.openapplications.org/oagis/9">
<ResponseCriteria>
<ResponseExpression actionCode="Never" expressionLanguage="xPath">*</ResponseExpression>
</ResponseCriteria>
</Show>
<ska:SalesOrder>
<SalesOrderHeader xmlns="http://www.openapplications.org/oagis/9">
<DocumentID>
<ID>141779</ID>
</DocumentID>
<RequestedShipDateTime>2009-11-04T07:00:54+01:00</RequestedShipDateTime>
</SalesOrderHeader>
</ska:SalesOrder>
</ska:DataArea>
</ska:ShowSalesOrder>
</env:Body>
</env:Envelope>
You can definitely still use the WCF infrastructure without requiring type definitions for all of the various messages. WCF specifically supports this through the Message class. Using it is not all that difficult. Here's some more information about them but the idea is basically you would use XML readers and writers to read and write messages.
Using the Message Class
One way to do it is to create an XML skeleton template containing placeholders for the values. Read the XML and replace the values with those from your object. Post the resulting XML to the web service using HttpWebRequest.
Even that this approach might work I would strongly recommend you creating a WCF proxy class and using this instead even if the web service contains hundreds of methods and objects that are not used. As long as it is a valid WSDL, WCF will handle it. Also if there are any changes to the web service all you have to do is regenerate the proxy. To avoid the ugliness of this web service create your own infrastructure that exposes only the useful methods and classes and hides the real call.
Related
My boss asked how long it would take to build a client to access a web service that will send and receive some basic data and embedded documents. Just starting playing with it to see what's involved. I have been doing web and desktop development for about 20 years but have literally never touched a web service so with that I'm at the extreme newb level.
So far I used the wsdl to create the ServiceReference1 and I can see the methods in intellisense but I don't have the first clue where to start with calling the methods, passing parameters and consuming the response. I feel stupid because I'm sure it's pretty simple but just flailing at the code and looking for on point examples has gotten me nowhere. Usually I can find something through google in minutes that is exactly on point but not having luck here. Would appreciate a push in the right direction.
So basic questions. Proper way to make the calls. How and where to land the returned data. How to add parameters.
Here is my first attempt. This gets a simple list and has no parameters. The result in fiddler returns data but there is a runtime type mismatch error which I think is caused by some stray characters leading the response which appear to be caused by chucking, what ever that is. The response starts with 1ffs every time then contains the remainder of the xml. Secondarily I need to get the list into a dataset or some other container but I was hoping to just be able to step into the code and see a result
ServiceReference1.FilingInfoClient webservice = new FilingInfoClient();
ServiceReference1.courtListRequest cr = new ServiceReference1.courtListRequest();
ServiceReference1.courtListResponse lr = new ServiceReference1.courtListResponse();
lr = webservice .getCourtList(cr);
This is essentially the same but takes a date param. When I run this fiddler shows the parameter is not being sent. No other errors but I'm sure only because it exploded immediately.
ServiceReference1.FilingInfoClient webservice = new FilingInfoClient();
ServiceReference1.messageListRequest mr = new ServiceReference1.messageListRequest();
ServiceReference1.MessageListResponse mlr = new ServiceReference1.MessageListResponse();
mr.latestMessagePullTimestamp = DateTime.Now.AddDays(-5);
mr.endTimestamp = DateTime.Now;
mlr.latestMessagePullTimestamp = DateTime.Now;
mlr = webservice.getMessageList(mr);
This is the info provided by the web service host
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn1="urn:green:partner:ws:schema:FilingInfo">
<x:Header/>
<x:Body>
<urn1:getcourtList>
<urn1:courtListRequest/>
</urn1:getcourtList>
</x:Body>
</x:Envelope>
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn1="urn:green:partner:ws:schema:FilingInfo">
<x:Header/>
<x:Body>
<urn1:getMessageList>
<urn1:messageListRequest>
<urn1:latestMessagePullTimestamp>?</urn1:latestMessagePullTimestamp>
</urn1:messageListRequest>
</urn1:getMessageList>
</x:Body>
</x:Envelope>
we've got request and response pairs for each operation in the webservice. think like request => input, response => output, operation => method.
the webservice is an API. things that consume APIs are clients. the WSDL describes the API's operations and their requests and responses. tools like visual studio know how to read WSDLs and build C# code to perform those (SOAP) operations under-the-hood. this is the client (here FilingInfoClient). visual studio'll also generate classes representing each request and response.
this allows for a familiar programming experience. you call a method, give it some input, and it returns some output.
using (var client = new FilingInfoClient())
{
var request = new courtListRequest
{
//TODO fill in relevant properties
};
var response = client.getCourtList(request);
}
Can anybody explain to me what the following code does? Specifically, the attribute on the GetStatus method. I know it has something to do with SOAP requests, but I tried googling "SoapDocumentMethodAttribute" and didn't find much that explains things. Can anybody dumb it down for me please?
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://dummyurl.com/", RequestNamespace = "http://dummyurl.com/", ResponseNamespace = "http://dummyurl.com/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public string GetStatus(string Username, string Password, string EndSystemUser) {
object[] results = this.Invoke("GetStatus", new object[] {
Username,
Password,
EndSystemUser});
return ((string)(results[0]));
}
Soap services expose WSDL to the consumers which contain information about how SOAP messages would be written.
This WSDL can be written either in RPC style or in Document Style.
Document style is perfered over RPC style as it means less coupling and provides better way to validate the message.
This attribute instructs WSDL generator to use Document Style.
From the MSDN Documentation:
Web Services Description Language (WSDL) defines two styles for how an
XML Web service method, which it calls an operation, can be formatted
in a SOAP message: RPC and Document. Document refers to formatting the
XML Web service method according to an XSD schema. The Document style
refers to formatting the Body element as a series of one or more
message parts following the Body element.
Refer this link for examples of RPC / Document style.
I’ll provide a web service for a client with a given WSDL.
Unfortunately I’m not able to tell the serializer to accept unqualified elementForm.
I seek for the way to set the elementFormDefault to either "unqualified" or even "None" to hide it complete
I’ll receive something like
<NS:Request>
<some stuff>…</some stuff>
</NS:Request>
But I see no content in my request
Only if I Change the prefix or remove the prefix and change the NS scope by adding a new NS to the request
<NS:Request>
< NS:some stuff>…</ NS:some stuff>
</NS:Request>
or
< Request xmlns:myNamespace>
< some stuff>…</some stuff>
</Request>
The web service works fine.
Thanks for your support
Although this is a dead question from 2 years ago, I would still like to answer it as this is my first related search result when I encounter the exact same problem.
The WSDL file generated via .asmx?wsdl has an attribute
elementFormDefault=qualified
within it's schema tag, which forces the clients to add a namespace prefix to all input element if they were to successfully pass their input to server. (If the client ignore the namespace prefix regardless, the server will receive empty request with no input).
Since in my case my client could not generate a qualified soap request no matter what, I have to change on server side.
The way you do it is to add
[XmlElement(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
in front of every input parameter for every web method:
[WebMethod]
public string TestMethod(
[XmlElement(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]string input1,
[XmlElement(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]int input2)
{
/***code here****/
}
I am developing one webservice using c#.It is possible intercept the client request using Filter(Using HttpModule).But how it is possible to modify the request.I can get request like this
Stream InputStrm = App.Context.Request.InputStream;
i want to decrypt the request&Set it back.How can i do this??
It depends on the web service technology you're using. If you're using Web API or MVC, you use an ActionFilter. If you're using asmx, you use a SoapExtension. If you're using WCF, you have various extension points. If it's just a web request, an HttpModule can apply a filter by saying HttpContext.Current.Response.Filter = new SomeFilter( HttpContext.Current.Response.Filter ) where SomeFilter is a class like public class SomeFilter : Stream {. Request.Filter should work the same way. http://www.15seconds.com/issue/020417.htm is an old article, but shows a bit about these Response.Filter classes.
I am trying to consume the .net webservice from cold fusion. Methods having simple types working fine. But i am having problems with one particular method which accepts byte[] array as input.
Below the sample webmethod declaration
[WebMethod]
public AVStatus ScanStream(byte[] fileObject)
{
// code
}
and the cold fusion code consuming this service is
<cffile action="readBinary" file="#FileName#" variable="filedata">
<cfset b64file = #toBase64(filedata)#>
<cfinvoke webservice = "http://xxx/scanservice.asmx?wsdl"
method = "ScanStream"
returnVariable = "result">
<cfinvokeargument name="fileObject" value="#b64file#" />
</cfinvoke>
This always leads to this error Web service operation ScanStream with parameters cannot be found.
can someone help me out this?
It seems that the binary data has been exposed as bas64 string in the coldfusion while byte[] is exposed by the service as an XML array (of bytes).
Change the ScanStream (if you can) to accept a string, if web service is not yours you could convince owners to provide another method which accepts string and uses Convert.FromBase64String to change to byte array.
Webservices are remote, not public. Public allows access by other CF classes and pages. Change public to remote, and you should be able to "see" your webservice.