Some success with accessing Web Service via SOAP

SteveBee

Member
Licensed User
Longtime User
I've posted some problem I was having around this, so I thought I'd now post the insights and work-arounds that I've come to.

The WS method I was using for test was producing too many items, and I could not properly inspect the full returned string (because Log truncates, and I can't copy a variable in the debugger), so I put up a 'test' web service.

When I use an ASP.NET (proxy) as client, I get a nice clean return:

<Stores><Record><StoreName>Albany Creek</StoreName><Address>Shop 19, Albany Market Place,720 Albany Creek Road,Albany Creek QLD 4035</Address><Phone1>07 xxxx4334</Phone1><Phone2>SS - xxxx 661 140</Phone2><Fax>07 xxxx4313</Fax><Email>[email protected]</Email><StoreSysID>89</StoreSysID><Latitude>-27.345486</Latitude><Longitude>152.966995</Longitude></Record><Record><StoreName>Albury</StoreName><Address>Shop 3, 659 Young Street,Albury,NSW 2640</Address><Phone1>02 xxx2111</Phone1><Phone2></Phone2><Fax>02 xxxx32112</Fax><Email>[email protected]</Email><StoreSysID>117</StoreSysID><Latitude>-36.071899</Latitude><Longitude>146.925766</Longitude></Record><Record><StoreName>Altona</StoreName><Address>114 Millers Road,Altona,VIC 3025</Address><Phone1>03 xxxx522</Phone1><Phone2></Phone2><Fax>03 93144566</Fax><Email>[email protected]</Email><StoreSysID>120</StoreSysID><Latitude>-37.826740</Latitude><Longitude>144.848648</Longitude></Record></Stores>



But B4a has *no* in-built way of handling SOAP, so the return I get from it is this (below):

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><StoresListing_TESTResponse xmlns="http://cheesecake.com.au/"><StoresListing_TESTResult>&lt;Stores&gt;&lt;Record&gt;&lt;StoreName&gt;Albany Creek&lt;/StoreName&gt;&lt;Address&gt;Shop 19, Albany Market Place,720 Albany Creek Road,Albany Creek QLD 4035&lt;/Address&gt;&lt;Phone1&gt;07 xxxx4334&lt;/Phone1&gt;&lt;Phone2&gt;SS - xxx1 140&lt;/Phone2&gt;&lt;Fax&gt;07 xxx313&lt;/Fax&gt;&lt;Email&gt;[email protected]&lt;/Email&gt;&lt;StoreSysID&gt;89&lt;/StoreSysID&gt;&lt;Latitude&gt;-27.345486&lt;/Latitude&gt;&lt;Longitude&gt;152.966995&lt;/Longitude&gt;&lt;/Record&gt;&lt;Record&gt;&lt;StoreName&gt;Albury&lt;/StoreName&gt;&lt;Address&gt;Shop 3, 659 Young Street,Albury,NSW 2640&lt;/Address&gt;&lt;Phone1&gt;02 xxxx111&lt;/Phone1&gt;&lt;Phone2&gt;&lt;/Phone2&gt;&lt;Fax&gt;02 xxxx32112&lt;/Fax&gt;&lt;Email&gt;[email protected]&lt;/Email&gt;&lt;StoreSysID&gt;117&lt;/StoreSysID&gt;&lt;Latitude&gt;-36.071899&lt;/Latitude&gt;&lt;Longitude&gt;146.925766&lt;/Longitude&gt;&lt;/Record&gt;&lt;Record&gt;&lt;StoreName&gt;Altona&lt;/StoreName&gt;&lt;Address&gt;114 Millers Road,Altona,VIC 3025&lt;/Address&gt;&lt;Phone1&gt;03 xxxx22&lt;/Phone1&gt;&lt;Phone2&gt;&lt;/Phone2&gt;&lt;Fax&gt;03 xxxx566&lt;/Fax&gt;&lt;Email&gt;[email protected]&lt;/Email&gt;&lt;StoreSysID&gt;120&lt;/StoreSysID&gt;&lt;Latitude&gt;-37.826740&lt;/Latitude&gt;&lt;Longitude&gt;144.848648&lt;/Longitude&gt;&lt;/Record&gt;&lt;/Stores&gt;</StoresListing_TESTResult></StoresListing_TESTResponse></soap:Body></soap:Envelope>



So it occurred to me to just string-slice out the part that I need, replace 'encoded' characters, and present that as XML to the parser.

B4X:
Sub JobDone (Job As String)
   Dim s1 As String, s2 As InputStream, p1 As Int, p2 As Int, xmlname As String
   If HttpUtils.IsSuccess(WS_url) Then
      s1 = HttpUtils.GetString(WS_url)
      s1 = s1.Replace("&lt;", "<").Replace("&gt;", ">")
      p1 = s1.IndexOf("<Stores>")
       p2 = s1.IndexOf("</Stores>")
       s1 = s1.SubString2(p1, p2 +9)
      xmlname = "ws_resp.xml"
      File.WriteString(File.DirInternalCache, xmlname, s1)

      s2 = File.OpenInput(File.DirInternalCache, xmlname)

      parser.Parse(s2, "Parser")
   End If
End Sub


Works like a charm!
Perhaps this may help someone else.. also happy to hear of any better ways to achieve the outcome

Steve
 

kanaida

Active Member
Licensed User
Longtime User
I was trying to do the same but got stuck on the request

Hey, I think I may be able to merge my solution with yours.

Instead of using the typical web service, I made an asp.net web site (a blank one). Then I created an http handler (main.ashx)

This returns pure xml with no nonsense soap, it's basically xml serialization of objects, returned directly into the response. No soap at all:

B4X:
<%@ WebHandler Language="VB" Class="main" %>

Imports System
Imports System.Web

Public Class main : Implements IHttpHandler
    
    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        context.Response.ContentType = "application/xml"
        
        Dim SB As New Xml.Serialization.XmlSerializer(GetType(List(Of ListViewItem)))
        
        Dim ListView As New List(Of ListViewItem)
        Dim L As New ListViewItem
        L.Label1 = "Google"
        L.Label2 = "Web"
        L.Value = "http://www.google.com"
        ListView.Add(L)
        
        Dim L2 As New ListViewItem
        L2.Label1 = "Google"
        L2.Label2 = "Images"
        L2.Value = "http://www.google.com/images"
        ListView.Add(L)
        
        Dim MS As New IO.MemoryStream
        SB.Serialize(MS, ListView)
        
        'context.Response.Write("")
        context.Response.BinaryWrite(MS.ToArray)
        
    End Sub
 
    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class

Here is the ListView class (my own, not b4a's)
B4X:
Imports Microsoft.VisualBasic

Public Class ListViewItem
    Public Image As Byte()
    Public Label1 As String
    Public Label2 As String
    Public Value As String
End Class

What I would like to know is how to get the webclient/http library in B4a to stop giving me a "connection refused" error. I have it locally published to my localhost's IIS server, and it runs fine in any web browser if I got to it's URL.
I'm not sure if b4a's http library is giving a bogus error, or if something else is happening. I'm trying to set up an http proxy to watch the raw communications.

The main goal of this was to try to make the android gui dynamically based on what the web service says. So I just try to make an engine, where my web service defines what the interface does and what values are called from button clicks etc...

This is the raw data returned to my web browser (I think. after the proxy test i'll know for sure.)

B4X:
<?xml version="1.0"?>
<ArrayOfListViewItem xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ListViewItem>
    <Label1>Google</Label1>
    <Label2>Web</Label2>
    <Value>http://www.google.com</Value>
  </ListViewItem>
  <ListViewItem>
    <Label1>Google</Label1>
    <Label2>Web</Label2>
    <Value>http://www.google.com</Value>
  </ListViewItem>
</ArrayOfListViewItem>
 
Last edited:
Upvote 0

SteveBee

Member
Licensed User
Longtime User
Well I'm now able to manage SOAP in B4a - and all my web services *already* use it, so....I don't know how much help I can be to you....

I could send you some code to manage the SOAP POST wrapper? (The 'other end' of what I already posted).

The only other thing that comes to mind is: are you using the emulator?
Then see my other thread re. Problems with SSL.
It seems the emulator can get in the way sometimes, nothing to do with B4a or the code...

HTH
Steve
 
Upvote 0

kanaida

Active Member
Licensed User
Longtime User
can't seem to call it at all.

First, i'm just trying to call the web service. I figured out the connection error lol... I was calling "localhost" from my android device, instead of my pc's ip address. :).

Now that I did fix it, I get : Internal Server Error 500

I'm trying to use the following part of the .asmx description:

B4X:
HTTP POST

The following is a sample HTTP POST request and response. The placeholders shown need to be replaced with actual values.

POST /AnDyn/AnDyn.asmx/test HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: length

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfListViewItem xmlns="http://tempuri.org/">
  <ListViewItem>
    <Image>base64Binary</Image>
    <Label1>string</Label1>
    <Label2>string</Label2>
    <Value>string</Value>
  </ListViewItem>
  <ListViewItem>
    <Image>base64Binary</Image>
    <Label1>string</Label1>
    <Label2>string</Label2>
    <Value>string</Value>
  </ListViewItem>
</ArrayOfListViewItem>

Dim req As HttpRequest
req.InitializeGet("http://localhost/AnDyn/AnDyn.asmx")
hc.Execute(req, 1)

How do I call a method called test?
I've tried, GET, POST, messing with headers, tons of stuff. Just can't get the web service to respond properly.
 
Upvote 0

kanaida

Active Member
Licensed User
Longtime User
Aha!

Dammit lol.. it's asp.net.
I think I got an easy way for this to work for both of us with NO soap parsing, and GET url's with simple parameters.

By default it does NOT have the GET command enabled from anywhere but your local machine.

Add this to web.config under configuration\system.web
<webServices>
<protocols>
<add name ="HttpGet"/>
</protocols>
</webServices>

And make a GET http request like so:

Dim req As HttpRequest
req.InitializeGet("http://Server/WebServiceFolder/service.asmx/MethodName")
HttpClient.Execute(req, 1)

For parameters, try like asp.net page parameters:
http://Server/WebServiceFolder/service.asmx/MethodName?param1=1234

I was returned "pure" xml. Nothing like you had to mess with. No soap to parse out.

Now, what would be helpful is how to read the xml.
I would also recommend returning objects/classes, NOT datasets. Datasets add crazy xml at the top.
Objects come back nice and clean.
 
Last edited:
Upvote 0

SteveBee

Member
Licensed User
Longtime User
Mate I don't know why you're bothering - I already posted how to get SOAPd web service results back.

I've got a couple of service results datas back and have happily parses the XML.

If I can be of some assistance let me know, otherwise I've moved on - it's all working for me.
 
Upvote 0

SteveBee

Member
Licensed User
Longtime User
BTW the recommendation from Erel is to use the HttpUtilty class, not the raw HttpClient as you've done.

Again, see my threads re. Web service

HTH
 
Upvote 0

kanaida

Active Member
Licensed User
Longtime User
Thanks

Thanks,
I've been able to call, and easily parse most xml in very few lines of code.
Not with any of your examples, that's what I would have done a while back ago. If it works it works. it got me going in the right direction either way.
I'll look into HttpUtility. I've only run the example project.
 
Upvote 0

Silentsea

Member
Licensed User
Longtime User
Hi Steve,

nice research and solution, thanks for sharing.

It is possible for you, to post your 'other end' as well? I've to reach the same goal, connect our existing methods (webservice) to an little android tool. A closer look would be nice.

Thanks in advance.
 
Upvote 0

SteveBee

Member
Licensed User
Longtime User
"It is possible for you, to post your 'other end' as well? I've to reach the same goal, connect our existing methods (webservice) to an little android tool. A closer look would be nice."

Ok, pls. find attached zip containing a StaticCode module.

(Note: this is a work-in-progress, so is untidy!)

The sub you want is "CallWebService", taking parameters for the WS url & method names, and a collection of key/value pairs containing parameters.

Here is a sample usage:
B4X:
   Dim Params As Map
   Params.Initialize
   Params.Put("_systemUserID", 117)
   Params.Put("_status", 0)            
   XMLutils.CallWebService("ListMobileJobsByStatus", Params, WS_url)

In your callback, you use a corresponding sub "GetWebServiceDataStream" to get an input stream (which may be passed of to a parser) something like this:
HTML:
    sxParser.Parse(XMLutils.GetWebServiceDataStream("MobileJobs", WS_url, action), "sxParser")


This is all working beautifully for me.
(I can't get the "CallWebServiceXML" method to work reliably without Bad Request/400 errors - but that's another story)

HTH... pls. advise of any more forward movement you make that is compatible with this approach.

cheers
Steve
 

Attachments

  • XMLutils.zip
    1.4 KB · Views: 494
Upvote 0

kanaida

Active Member
Licensed User
Longtime User
Now I see what you were doing. You're actually trying to implement raw SOAP request/replies. It's however not necessary at all.

Rather than calling the web service how you're doing it now, I just do it with an http GET request. It won't return any of that SOAP header garbage at all. It will just return you the actual xml that makes up whatever object you're getting back.

I call mine by forming a simple URL:

B4X:
Http://MyWebsite.com/WebServicePath/service.asmx/GetAllReports?Parameter1=xxx&Parameter2=xxx

And what I get back is very straight to the point:
<Report>
<Name>Report1</Name>
<Id>23</Id>
<Report>

ASP.net returns data differently when you POST vs GET

So in the end I end up parsing much less, and chewing up less cpu and mem
 
Upvote 0

SteveBee

Member
Licensed User
Longtime User
GET? You shouldn't be using it, except for local site testing.

Recommended advice for better Web Service security is to disable GET & PUT - look it up!

Thus SOAP...
 
Upvote 0

kanaida

Active Member
Licensed User
Longtime User
I only use web services across SSL, I always pass an encrypted token for authentication, within the encrypted tunnel so it's as safe as its gonna get.
My web service also bans ip addresses of people with multiple incorrect tokens being passed for a certain period of time, that absolutely kills brute force attacks.

By the way, soap doesn't make anything any safer. Contrary to what you may have read. I believe WCF has some extra goodies in it as far as security, but not regular web services. I could just make a console app, make a web reference and go to town on any web service until I get in.

another note, you're using POST lol... it's the same as a GET except for how you put together the data you pass. It's just another way of doing the same thing.
 
Last edited:
Upvote 0

kanaida

Active Member
Licensed User
Longtime User
Here's an excerpt from the horse's mouth W3C URIs, Addressability, and the use of HTTP GET and POST

I recommend you try WireShark so you can see the raw data going over the wire. If you can read anything important, it's not safe

1.3 Quick Checklist for Choosing HTTP GET or POST

Use GET if:
The interaction is more like a question (i.e., it is a safe operation such as a query, read operation, or lookup).
Use POST if:
The interaction is more like an order, or
The interaction changes the state of the resource in a way that the user would perceive (e.g., a subscription to a service), or
The user be held accountable for the results of the interaction.
However, before the final decision to use HTTP GET or POST, please also consider considerations for sensitive data and practical considerations.


4 Considerations for Sensitive Data

Some Web interactions involve sensitive data, such as passwords, credit card numbers, social security numbers, and bank account numbers (as in scenario 2).

To protect information carried by either GET or POST operations, it is often appropriate to use an underlying secure protocol such as the Secure Socket Layer [SSL3]. By using GET over SSL for safe operations, designers retain some of the benefits of URI addressability, even if they lose others (e.g., caching). Designers do need to consider the costs of using SSL, such as:

Time required to establish an SSL connection.
Memory required on the server to create a novel copy of (encrypted) data for each request.
In situations where the use of such protocols for security is inappropriate, designers MAY use POST to carry credentials or other information needed to authenticate an otherwise safe operation. For instance, a designer may require security beyond the protocol layer into the application layer (e.g., because software or data queues within a server site are not trusted, or because the application requires credentials not supported at a lower layer).

When the use of SSL is impractical, it may still be possible to keep some sensitive data out of a URI. For instance, designers can communicate authentication information in HTTP headers rather than in the query part of a URI.

Broad issues of data protection such as preventing unauthorized access to an individuals' bookmarks (or cookies, for that matter) lie beyond the scope of Web architecture.
 
Upvote 0

SteveBee

Member
Licensed User
Longtime User
Yes, I know all this.. am using SSL... and my data qualifies for all the 'sensitivities' mentioned in POST qualification.

I am so past this... have code that works.. is secure x2...
Can we now move on and do something else?

This will be my last response on this aspect of the topic.
 
Upvote 0

SteveBee

Member
Licensed User
Longtime User
Sorry Geordie, most of it is now in a dedicated app. But I posted the low level "Utils" module thus morning, with example calls - what else would you need? (g)

Give it a try... If you run into problems just re-post, and I'll try to assist.

You could even try the non-Soap method (if your web service interface permits).
 
Last edited:
Upvote 0

SteveBee

Member
Licensed User
Longtime User
I don't think you should be thinking in terms of WCF or asmx or whatever when considering the Android app.

The question is: what is the web service published interface?

You get this when you issue (for an asmx version) something like
Http://myserver.com/myService.asmx

Dunno what a WCF interface is like but that's what you need. It's not the Android app's brief to any more than work with that interface.

Hth
 
Last edited:
Upvote 0
Top