Is there a straightforward way to query a web service to see which messages it supports? The C# .NET application I'm working on needs to be able to handle an older version of the web service, which does not implement the message I'm trying to send. The web service does not expose a version number, so Plan B is to see if the message is defined.
I'm assuming I can just make an HTTP request for the WSDL and parse it, but before I go down that path, I want to make sure there's not a simpler approach.
Update:
I've decided to get the WSDL and get messages directly. Here's the rough draft for getting all the messages:
HttpWebRequest webRequest = (HttpWebRequest) WebRequest.Create( "http://your/web/service/here.asmx?WSDL" );
webRequest.PreAuthenticate = // details elided
webRequest.Credentials = // details elided
webRequest.Timeout = // details elided
HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse();
XPathDocument xpathDocument = new XPathDocument( webResponse.GetResponseStream() );
XPathNavigator xpathNavigator = xpathDocument.CreateNavigator();
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager( new NameTable() );
xmlNamespaceManager.AddNamespace( "wsdl", "http://schemas.xmlsoap.org/wsdl/" );
foreach( XPathNavigator node in xpathNavigator.Select( "//wsdl:message/#name", xmlNamespaceManager ) )
{
string messageName = node.Value;
}
Parsing the WSDL is probably the simplest way to do this. Using WCF, it's also possible to download the WSDL at runtime, essentially run svcutil on it through code, and end up with a dynamically generated proxy that you can check the structure of. See https://learn.microsoft.com/en-us/archive/blogs/vipulmodi/dynamic-programming-with-wcf for an example of a runtime-generated proxy.
I'm pretty sure WSDL is the way to do this.
Related
I’ve got 2 projects that run on different servers but both have a reference to a common dll that contains a complex type called LeadVM. LeadVM contains sub-objects, like Employer which contains properties. Ex:
LeadVM.FirstName
LeadVM.LastName
LeadVM.Employer.Name
LeadVM.Employer.Phone
So, project 1 can create a LeadVM type of object, and populate it. I need to then, via an HTTP call, POST in the data to a controller/action in the 2nd project. The 2nd project knows what a LeadVM object is.
How can I...serialize(?) LeadVM and pass it to the accepting Action in the 2nd project?
EDIT: Thanks to #Unicorno Marley, I ended up using the Newtonsoft JSON stuff.
Now I just create my object in project 1, then do the following code (I'm sure it's clunky but it works).
LeadVM NewCustomer = new LeadVM();
NewCustomer.set stuff here....
var js = Newtonsoft.Json.JsonSerializer.Create();
var tx = new StringWriter();
js.Serialize(tx, NewCustomer);
string leadJSON = tx.ToString();
And then I can use HttpWebRequest to send a web request to my project 2.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:1234/Lead/Create");
request.Method = "POST";
StreamWriter streamOut = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
streamOut.Write(System.Web.HttpUtility.UrlEncode(leadJSON));
streamOut.Close();
HttpWebResponse resp = null;
resp = (HttpWebResponse)request.GetResponse();
StreamReader responseReader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8);
sResponse = responseReader.ReadToEnd();
resp.Close();
In project 2, I can catch the json sent like this and my NewCustomer object in project 2 is already populated, ready for use.
var buffer = new byte[Request.InputStream.Length];
Request.InputStream.Read(buffer, 0, buffer.Length);
string json = System.Text.Encoding.Default.GetString(buffer);
json = System.Web.HttpUtility.UrlDecode(json);
LeadVM NewCustomer = Newtonsoft.Json.JsonConvert.DeserializeObject<PhoenixLead.LeadVM>(json);
I'm sure I'm doing things very awkwardly. I'll clean it up, but wanted to post the answer that I was led to.
Json is probably the most common option, there is a good json library for c# that can be found here:
http://james.newtonking.com/json
Alternatively, since youre just doing an HTTP post and you only have one object, the fastest option would just be to write out the data line by line and have the recipient machine parse it. Since they both know what a LeadVM is, and both presumably have the same definition, it would be trivial to read a text string into the proper variables. This quickly becomes the slower option if you decide to add more object types to this process though.
I am trying to post XML to a web service directly rather than using VS's "add a web reference..." functionality. There is a good reason for this. To get proper test XML I logged what was generated using the standard web reference. I used what I found in this question Capturing SOAP requests to an ASP.NET ASMX web service
The web service was just what is generated when you create a new web service in visual studio, nothing fancy.
To try and pass the XML itself, I borrowed the code from this question Client to send SOAP request and received response
Unfortunately, when I call it, I get "The remote server returned an error: (500) Internal Server Error." 100% if I call the same web method like this
WebService1 ws = new WebService1();
String output = ws.HelloWorld();
then everything works great so I know the service is working well as deployed. I would appreciate so greatly some advice as to what I am doing wrong =)
Here is my code:
var _url = "http://localhost/simulator/webservice1.asmx";
var _action = "http://localhost/simulator/webservice1.asmx?op=HelloWorld";
String soapMessage = soapMessage = #"<?xml version='1.0' encoding='utf-8'?><soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'><soap:Body><HelloWorld xmlns='http://tempuri.org/' /></soap:Body></soap:Envelope>";
XmlDocument soapEnvelop = new XmlDocument();
soapEnvelop.LoadXml(soapMessage);
XmlDocument soapEnvelopeXml = soapEnvelop;
HttpWebRequest webRequest = CreateWebRequest(_url, _action);
InsertSoapEnvelopeIntoWebRequest(soapEnvelopeXml, webRequest);
// begin async call to web request.
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
// suspend this thread until call is complete. You might want to
// do something usefull here like update your UI.
asyncResult.AsyncWaitHandle.WaitOne();
// get the response from the completed web request.
string soapResult;
using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult))
using (StreamReader rd = new StreamReader(webResponse.GetResponseStream()))
{
soapResult = rd.ReadToEnd();
}
We have a new accounting system that provides webservice interface to external clients. I want to access one of the interfaces but there's no WSDL so i created the request through the use of HttpWebRequest and it works fine.
However to ease the creation of the requests and parsing of the response i would like to create some kind of automapping function. I have the request and response structure in an XSD file. Any ideas?
Request creation:
public void SendRequest()
{
HttpWebRequest request = CreateWebRequest();
XmlDocument soapEnvelopeXml = new XmlDocument();
soapEnvelopeXml.LoadXml(#"<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
<soap:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
++ structure type inserted here ++
</soap:Body>
</soap:Envelope>");
using (Stream stream = request.GetRequestStream())
{
soapEnvelopeXml.Save(stream);
}
using (WebResponse response = request.GetResponse())
{
using (StreamReader rd = new StreamReader(response.GetResponseStream()))
{
string soapResult = rd.ReadToEnd();
Console.WriteLine(soapResult);
}
}
}
Well, if you really have no way of getting hold of a proper WSDL file but have XSD:s you could probably use the xsd.exe tool to create classes that map to your request and response types.
Something like this (run this in a Visual Studio Command Prompt)
xsd.exe TheRequest.xsd /c /n:Your.Namespace
xsd.exe TheResponse.xsd /c /n:Your.Namespace
But really, try your best to find that WSDL, it will make your life a lot easier..
I recommend that you Create a WSDL - reverse engineer it, and then produce your webservices client stubs from it.
The way to do it:
Get a wire trace of the exact request and response the webservice uses. Use Fiddler or something similar.
Build a WCF service, that approximates the request and response.
Get the trace of that
Compare (3) to (1). Remember you want XML infoset equivalence. Your namespace prefixes need not be the same, for example. Your namespaces need not be declared in the same location. But, the element names need to be exactly the same, and the xmlns strings need to be exactly the same. Modify the WCF service to more closely match the original. Restart it, go to step 3. Iterate as many times as it takes.
point your WCF client to the real web service
sit back and relax.
I've done this and it works. It can be tricky if you don't have insight into how DataContract attributes affect the XML on the wire. But as you play with it, you will gain the insight pretty quickly.
But how is it possible that it is a SOAP service and there is no WSDL on the service side? It might save you a ton of work to re-double your efforts to locate the real WSDL.
In a project I'm invovled in, there is a requirment that the price of certain
stocks will be queryed from some web interface and be displayed in some way.
I know the "query" part of the requirment can be easily implemented using a Perl module like LWP::UserAgent. But for some reason, C# has been chosen as the language to implement the Display part. I don't want to add any IPC (like socket, or indirectly by database) into this tiny project, so my question is there any C# equivalent to the Perl's LWP::UserAgent?
You can use the System.Net.HttpWebRequest object.
It looks something like this:
// Setup the HTTP request.
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create("http://www.google.com");
// This is optional, I'm just demoing this because of the comments receaved.
httpWebRequest.UserAgent = "My Web Crawler";
// Send the HTTP request and get the response.
HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
if (httpWebResponse.StatusCode == HttpStatusCode.OK)
{
// Get the HTML from the httpWebResponse...
Stream responseStream = httpWebResponse.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
string html = reader.ReadToEnd();
}
I'm not sure, but are you simply trying to make an HTTP Request? If so, you can use the HttpWebRequest class. Here's an example http://www.csharp-station.com/HowTo/HttpWebFetch.aspx
If you want to simply fetch data from the web, you could use the WebClient class. It seems to be quite good for quick requests.
Here's the proxy method that was created for the web service I'm trying to access. How would I go about modifying it to get the raw XML from the web service call?
/// <remarks/>
[System.Web.Services.Protocols.SoapHeaderAttribute("CallOptionsValue")]
[System.Web.Services.Protocols.SoapHeaderAttribute("MruHeaderValue")]
[System.Web.Services.Protocols.SoapHeaderAttribute("SessionHeaderValue")]
[System.Web.Services.Protocols.SoapHeaderAttribute("QueryOptionsValue")]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", RequestNamespace = "urn:partner.soap.sforce.com", ResponseNamespace = "urn:partner.soap.sforce.com", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("result")]
public QueryResult query(string queryString)
{
object[] results = this.Invoke("query", new object[] {
queryString});
return ((QueryResult)(results[0]));
}
Thanks for your help!
Fortunately there is a nice way to do it, just modify the generated proxy class so it inherits from different base. The alternative implementation comes from Web Services Enhancements 3.0 pack:
Microsoft.Web.Services3.WebServicesClientProtocol
in the class you'll have RequestSoapContext.Envelope.InnerXml and ResponseSoapContext.Envelope.InnerXml in the scope - that's exactly what you need.
If you want just make a dump using the Fiddler Web Debugging tools.
If you want to really retrive/process raw XML then proxy method will not help you. Create System.Net.HttpWebRequest for the web service, call it, and retrive pure XML response. Format/structure can be found at .ASPX page or web service documentation.
Well, as far as I remember, a buddy of mine that this once with regular ASMX webservices, and it was quite a hack....
How about just serializing the result you get back into XML? That might do the trick...
Something along the lines of (totally untested and from memory):
MemoryStream stm = new MemoryStream();
XmlSerializer xmlSer = new XmlSerializer(typeof(QueryResult));
xmlSer.Serialize(stm, queryResult);
Would that work?
Marc