This post was written by Kurt Rayner, Alpha Software's VP of Research and Development
With Alpha Anywhere you can create sophisticated applications efficiently by leveraging the development and runtime environment to integrate some often complicated technology without having to become an expert in every area.
This post will discuss how Alpha Anywhere makes web services more approachable without limiting your access to advanced features.
What are Web Services?
Calling a function on one computer (the server) from another computer (the client) is a technique that became popular as soon as it was possible to communicate from one computer to another. In principal, it is a very simple concept. In practice it can lead to some serious frustration!Take for example calling a function to retrieve all outstanding orders as of a particular date and time. In code, it might look something like:
Server.GetOutstandingOrders(now())
In order to make this work we need to be able to do the following:
- Find the server and establish a connection to it..
- Format the data values (parameters and results) to be exchanged so the client and server both understand them.
- Identify the functions available on the server.
- Transfer the request and values from the client to the server and receive the response back from the server.
Data Formats
Before we talk about the how, let’s look at the two most common formats for data, XML and JSON. XML (Extensible Markup Language) uses tags wrapped in angle brackets. JSON uses soft braces ({}) and can be parsed by JavaScript, so it is easier to read and to use directly in client side web code.Both XML and JSON represent not just single values or single rows, but entire collections of objects and the hierarchical relationships between them. This works well for transferring complex values to and from servers.
Here is an example of XML:
<Customer>
<LastName>Smith</LastName>
<FirstName>John</FirstName>
<Orders>
<Order>
<OrderNumber>12345</OrderNumber>
</Order>
</Orders>
</Customer>
<LastName>Smith</LastName>
<FirstName>John</FirstName>
<Orders>
<Order>
<OrderNumber>12345</OrderNumber>
</Order>
</Orders>
</Customer>
Customer : {
lastName : “Smith”,
firstName: “John”,
Orders: [ { orderNumber: 12345” } ]
}
lastName : “Smith”,
firstName: “John”,
Orders: [ { orderNumber: 12345” } ]
}
Protocols - How We Get There From Here
Currently, the two most popular methods for accomplishing all of this are SOAP (Simple Object Access Protocol) and RESTful (Representational State Transfer) web service calls.As the name suggests, both of these service calls are used on the internet, specifically using the HTTP protocol (or HTTPS for TLS security). Because of this, making requests and receiving responses is possible in the context of web browsers as well as any programming environment that can send and receive web requests. We can take advantage of TLS (formerly SSL) security, find servers by looking up the name (DNS names), take advantage of well-known ports (getting past fire-walls) and transfer the request using the HTTP protocol.
Piggy-backing on HTTP addresses many technical hurdles!
SOAP Services
SOAP services are functionally oriented. The caller invokes a function and passes object parameters and receives values back. Using XML to define the request and the data to be transferred, the request is sent to the server using the HTTP POST command. SOAP services have been around for quite a while. As a result, there are a number of (largely incompatible) implementations of SOAP. They are not perfect, but they are quite common.The XML syntax is fairly complicated for formatting requests and most developers use generated software on the client and the server (referred to as the “proxy” and “stub” respectively) to call and implement web services.
The definition of the service is also expressed in XML or in this case WSDL (Web Service Definition Language). WSDL is a machine readable definition of the service, data formats and many implementation details. Although most tools will generate WSDL, it is often coded by hand, and almost as often coded incorrectly.
For more on SOAP, you can read the Microsoft article.
RESTful Web Services
RESTful web services focus on the object itself (or a collection of objects) and implement five basic verbs (based on the HTTP protocol itself):- GET – Retrieve and object or collection of objects
- PUT – Update or replace an object
- POST – Create a new object
- PATCH – Update partial resources.
- DELETE – Delete an object
Because the basic syntax for RESTful calls is simpler and JSON is native to JavaScript, many requests can be hand coded explicitly.
For more on RESTful services read the excellent Dr. Dobb’s tutorial.
Client Libraries
Calling web services (especially SOAP services) can be quite complicated. RESTful services can be equally difficult when the network or the service is less than reliable or if a task may require many web calls. As a result, many implementers provide language specific libraries (C++, JavaScript, PHP, node.js, .NET, Java) that developers can use to connect to services and make requests as if the services were local to the client computer.Microsoft Windows Communication Foundation (WCF)
As the complexity of SOAP and RESTful web services and related standards virtually exploded, Microsoft introduced Microsoft Windows Communication Foundation as a way to abstract the details away for developers. The intent was to simplify the process and hide a lot of the complexity. Web service implementers think in terms of service contracts (sets of functions) and data contracts (objects).The areas of concern in WCF are:
- Address – Where is the service on the internet?
- Binding – How are we communicating with the service? Options can include MSMQ Queueing and more elaborate transports.
- Contract – What are the requests and data formats?
WCF has been open sourced, and it does not look like Microsoft will be investing much in the future in this technology, but many of the components from WCF can be used from .NET applications to implement and call web services.
One key component in WCF is the ability to read WSDL and to generate a .NET client (called a proxy) to call SOAP services.
How Alpha Anywhere Makes Calling SOAP Web Services Easier
Alpha Anywhere supports .NET libraries and has functions to retrieve WSDL and to generate WCF client proxies for simple SOAP, SOAP 1.2 and WCF services. New web development dialogs make it easy to generate client proxy assemblies (.NET libraries) and include them in your web project.You can register SOAP web services at the Web Projects Control Panel and then call methods of these services from your XBasic code.
The tools to create client assemblies and call web services are also available using XBasic. For example, the XBasic code below will create and register an assembly as well as instantiating the client object contained in the assembly and calling service functions through the client object:
To Create an Assembly:
dim Sv as DotNet::Services
Sv.GenerateWebServiceClientFromURL(
"http://hostname.com/service.svc?WSDL",
"c:\temp\myproxy.dll")
Note: Or GenerateWCFServiceClientFromURL()
dim Sv as DotNet::Services
Sv.GenerateWebServiceClientFromURL(
"http://hostname.com/service.svc?WSDL",
"c:\temp\myproxy.dll")
Note: Or GenerateWCFServiceClientFromURL()
To Register an Assembly In The XBasic Type System
dim Sv as DotNet::Services
dim Assy as DotNet::AssemblyReference
Assy.FileName = “c:\temp\myproxy.dll”
Sv.RegisterAssembly(“::Serv”, Assy)
dim Sv as DotNet::Services
dim Assy as DotNet::AssemblyReference
Assy.FileName = “c:\temp\myproxy.dll”
Sv.RegisterAssembly(“::Serv”, Assy)
To Instantiate the Client and Call a Web Service
dim Client as ::Serv::Client
Result = Client.CallFunction(arguments…)
dim Client as ::Serv::Client
Result = Client.CallFunction(arguments…)
WCF services (invoked with a .svc suffix in the web address) generate a proxy using a request/response style. These functions take a single object (the request) as a parameter and return a single object (the response). To make the call, you DIM a request object and set any values (or objects) into the request. When the call completes, the response will have status information and any data returned from the service call.
Here is a simple example of a request and response pattern:
dim Request as WorldPay::ProcessCCSaleRequest
Request.ccinfo = new WorldPay::CreditCardInfo()
Request.ccinfo.acctid = "TEST0"
Request.ccinfo.ccname = "Tony Test"
Request.ccinfo.ccnum = "5454545454545454"
Request.ccinfo.amount = 3.00
Request.ccinfo.expmon = 03
Request.ccinfo.expyear = 2010
Response = Client.ProcessCCSale(Request)
?response.ProcessCCSaleReturn
authcode = "DECLINED:1000420001:VALID PIN REQUIRED:"
Request.ccinfo = new WorldPay::CreditCardInfo()
Request.ccinfo.acctid = "TEST0"
Request.ccinfo.ccname = "Tony Test"
Request.ccinfo.ccnum = "5454545454545454"
Request.ccinfo.amount = 3.00
Request.ccinfo.expmon = 03
Request.ccinfo.expyear = 2010
Response = Client.ProcessCCSale(Request)
?response.ProcessCCSaleReturn
authcode = "DECLINED:1000420001:VALID PIN REQUIRED:"
Using .NET to Access Advanced Features
Because XBasic integrates tightly with .NET, you can access more advanced features like bindings and endpoints that need to be used to set security credentials, increase quotas and timeouts and to create complex types.Here are some examples:
Using WSHttpBindings for secure WCF services.
dim b as System::ServiceModel::WSHttpBinding
b.Security.Mode = System::ServiceModel::SecurityMode::None
…
dim e as System::ServiceModel::EndPointAddress = \
new System::ServiceModel::EndPointAddress("http://…")
dim MyClient as ::Serv::MyClient = new ::Serv::MyClient(b, e)
dim b as System::ServiceModel::WSHttpBinding
b.Security.Mode = System::ServiceModel::SecurityMode::None
…
dim e as System::ServiceModel::EndPointAddress = \
new System::ServiceModel::EndPointAddress("http://…")
dim MyClient as ::Serv::MyClient = new ::Serv::MyClient(b, e)
Using Basic Authentication with SOAP services:
dim b as System::ServiceModel::BasicHttpBinding
b.MessageEncoding = System::ServiceModel::WSMessageEncoding::Text
b.Security.Mode = System::ServiceModel::SecurityMode::Transport
…
dim e as system::ServiceModel::EndPointAddress = \
new System::ServiceModel::EndPointAddress("http://xxxxxxxxx")
dim MyClient as ::Serv::MyClient = new ::Serv::MyClient(b, e)
dim b as System::ServiceModel::BasicHttpBinding
b.MessageEncoding = System::ServiceModel::WSMessageEncoding::Text
b.Security.Mode = System::ServiceModel::SecurityMode::Transport
…
dim e as system::ServiceModel::EndPointAddress = \
new System::ServiceModel::EndPointAddress("http://xxxxxxxxx")
dim MyClient as ::Serv::MyClient = new ::Serv::MyClient(b, e)
Changing Timeouts:
dim b as System::ServiceModel::BasicHttpBinding
b.OpenTimeout = new System::TimeSpan(0,0,10) ' Connect
b.ReceiveTimeout = new System::TimeSpan(0,0,20) ' Inactivity
b.SendTimeout = new System::TimeSpan(0,1,0) ' Write Operation
b.CloseTimeout = new System::TimeSpan(0,0,15) ' Close Operation
dim b as System::ServiceModel::BasicHttpBinding
b.OpenTimeout = new System::TimeSpan(0,0,10) ' Connect
b.ReceiveTimeout = new System::TimeSpan(0,0,20) ' Inactivity
b.SendTimeout = new System::TimeSpan(0,1,0) ' Write Operation
b.CloseTimeout = new System::TimeSpan(0,0,15) ' Close Operation
Changing Quotas
dim b as System::ServiceModel::BasicHttpBinding
b.MaxReceivedMessageSize = 10485760
b.maxBufferSize = 10485760
b.MaxBufferPoolSize = 10485760
b.ReaderQuotas.maxDepth = 32
b.ReaderQuotas.MaxStringContentLength = 10485760
b.ReaderQuotas.MaxArrayLength = 16384
b.ReaderQuotas.MaxBytesPerRead = 4096
b.ReaderQuotas.maxNameTableCharCount = 16384
dim b as System::ServiceModel::BasicHttpBinding
b.MaxReceivedMessageSize = 10485760
b.maxBufferSize = 10485760
b.MaxBufferPoolSize = 10485760
b.ReaderQuotas.maxDepth = 32
b.ReaderQuotas.MaxStringContentLength = 10485760
b.ReaderQuotas.MaxArrayLength = 16384
b.ReaderQuotas.MaxBytesPerRead = 4096
b.ReaderQuotas.maxNameTableCharCount = 16384
Creating and Passing Arrays of Standard Types
'Get an image and put it into a binary array
fname = "c:\temp\checkmark.jpg"
img = file.to_blob(fname)
dim arrlist[1] as b
arrlist[] = img
'Get an image and put it into a binary array
fname = "c:\temp\checkmark.jpg"
img = file.to_blob(fname)
dim arrlist[1] as b
arrlist[] = img
Creating Arrays of Custom Types
dim ColDtl as ::MyService::SomeCustomType
cda = System::Array::CreateInstance(ColDtl.GetType(),1)
cda[1] = ColDtl
dim ColDtl as ::MyService::SomeCustomType
cda = System::Array::CreateInstance(ColDtl.GetType(),1)
cda[1] = ColDtl
Comment