Tuesday, September 21, 2010

REST Web Services from UniBasic

In the midst of working up some labs for Perspectives I started thinking about REST web service access from UniBasic. The lab I happened to be working-up is on SOAP based RPC web service usage, for which we have a number of working examples and implemented projects.

But in creating services for Manage 2000 internal consumption I have all but abandoned SOAP RPC in favor of the far more elegant JSON REST model. I am usually working in ASP.NET and IE DOM client land, thankfully with the prototype.js library. So the question of the day was what would REST JSON access look like from UniBasic and what are the central issues in using it?

Well in addition to all the SOAPRequest support added with the UniBasic Extensions are a couple of simple little commands for retrieving the content from a URL. Now URLs often return HTML intended for human consumption and are not very easy to parse. The JSON REST model however returns very nice orderly string serializations.

With something as simple as:

CREATE.REQUEST.RTN.CODE = createRequest(URL:"?":QS,HTTP.METHOD,HTTP.REQUEST.HANDLE)
SUBMIT.REQUEST.RTN.CODE = submitRequest(HTTP.REQUEST.HANDLE,'','',HTTP.RESPONSE.HEADERS,HTTP.RESPONSE.DATA,HTTP.RESPONSE.STATUS)

you can get orderly responses like:

HTTP.RESPONSE.DATA = {'oValItem':{'FileName':'CM', 'TableNbr':'', 'ItemId':'1024
', 'NewItemId':'1024', 'Valid':'True', 'Display':'Sears Systems, Incorporated',
'ErrorFlag':'False', 'ErrorMsg':''}}

UniData does not yet include a JSON DOM to match the XML DOM of the UniBasic Extensions, but the parsing difficulties of JSON serializations are relatively minor. And if you need to directly access JSON REST services in the middle of UniBasic code this seems like nice direct approach.

The real stumbling blocks aren't coding difficulties, but security of your application server if you open up the HTTP or HTTPS ports for UniBasic to "see" services on the Internet. One answer to this is to go through a proxy server. This also points out one of the benefits of the architecture of Manage 2000 with its ASP.NET arm which may be used in a similar manner to delegate interaction with the messy world away from your closely guarded business database into a DMZ area.

Thursday, September 2, 2010

Dynamic Dropdowns and TM JSON arrays

Whether you call it RIA or Web 2.0 or AJAX enabled, there is an evolutionary process taking place on the web these days with UIs morphing from BLOCK-TERM like full page postbacks to much more granular dynamic changes in the page as the user interacts with it. And you can certainly see the effects of this progression in Manage 2000 release 7.3.

A developer friend asked if I preferred coding in VB in the code-behind ("code-beside" in the new parlance) or on the client using javascript and such. This is one of those questions that makes my brain churn for awhile.

The central conclusion I finally came to was that if it makes the UI more convenient for the user, that I dynamically change control configurations (like reloading drop down options based on previous answers they've selected) as they progress through a page then I prefer taking manual control on the client using javascript. I still use code-behind and aspx templates to push the HTML out in the first place, but then shift to javascript-AJAX-JSON-DHTML to make the user interface reactive and dynamic so that it responds more intelligently to the conversation that the user is having with it without having to pause and reprocess major Page construction code.

So how does one go about dynamically loading an HTML SELECT with options from a Manage 2000 TM table?

I have added a new service called GetTMTable in /mt/JSONServices for just this purpose, using much of the same code as my last post:

Private Function GetTMTableAsJSONArray(ByVal context As HttpContext) As System.Text.StringBuilder
Dim TableNbr As String = context.Request.QueryString("TableNbr")
Dim result As New System.Text.StringBuilder
Dim arTableEntries As New System.Collections.Generic.List(Of Array)
Dim ds As New ROISystems.Components.roiDataSet
Dim TableMaster As New ROISystems.WebControls.roiTableMaster
ds = TableMaster.GetTable(TableNbr)
For Each entry As DataRow In ds.Tables("VALIDATION_Validation_Info").Rows
Dim row() As String = {entry.Item("Code"), entry.Item("Desc")}
arTableEntries.Add(row)
Next
Dim JSONSerializer As New System.Web.Script.Serialization.JavaScriptSerializer
result.Append(JSONSerializer.Serialize(arTableEntries))
Return result
End Function

Here is the js portion of my testcode.
function LoadTable() {
var Site = document.location.pathname.Field('/', 2, 1);
var svcUrl = document.location.protocol
+ '//' + document.location.host
+ '/' + Site + '/MT/JSONServices/GetTMTable.ashx';
var TableNbr = $F('TableNbr');
var qs = 'TableNbr=' + TableNbr + '&Cid=' + $F('hedtcid');
new Ajax.Request(svcUrl + '?' + qs, {
method: 'get', asynchronous: false,
onSuccess: function(transport) {
var arTableEntries = transport.responseJSON;
// clear and reload the dropdown with the new table
$('ddlTMTable').options.length = 0
$A(arTableEntries.each(function(item) {
var opt = document.createElement('option');
opt.text = item[1];
opt.value = item[0];
$('ddlTMTable').options.add(opt);
}))
}
});
}


The result is blindingly fast reloads of the dropdown list from various tables.

When you do finally postback you will run into some MS security checking unless you disable event checking in the page declaration in the aspx file, or in a web config setting:
@ Page EnableEventValidation="false"
or
pages enableEventValidation="false"

You may also run into occasional confusion during postback on the part of webcontrols code trying to figure out why what is coming back doesn't match what was sent out. To avoid this confusion you can either just use plain ol HTML controls or check the Request.Form("lbID") array directly if it gets to be a problem.

Unfortunately I had to change the roiTableMaster control to remove a dependency on roiPage so that it would work out of JSONServices. This makes it difficult to patch, but it is all better for 7.3 sp2.

In the mean time you could, of course, implement a version of /mt/JSONServices that descends from roiPage rather than IhttpHandler, it just would have all the application overhead that roiPage carries around.