| Alternate to Handlerequest Methodology as Presented in the Hercules Example |
|
|
|
| Contributed by Tony McGuire | |
| 09 May 2002 | |
|
Tony shows how to get rid of that growing switch..endSwitch statement and use a more flexible method of responding to incoming HTTP requests.Alternate to Handlerequest Methodology as Presented in the Hercules Example © 2002 Tony McGuire Download sample files demonstrating the methodology presented below. The Hercules example/model is wonderful. It gives us incredible flexibility to handle incoming requests. And using server.db allows us to automatically open/reference the correct library to handle the incoming request, through the 'handlerequest' method. My only issue has been all those case... statements. Likely, though, if you have that many case statements you need to create a new library for handling additional methods. However, this just bugged me. So I went looking for an alternative. Thus the recent thread on the pnews news server on execMethod(). In a library, you cannot use execMethod() to execute an internal method, unless the construct that has the library open (i.e., a form or script) 'knows about' that method. This would normally be done in that script/form's Uses clause. However, Tom Krieg discovered that the library can open a copy of itself, and that copy can be used by the one opening it; at this point, execMethod() works just fine. So, I went looking for a way to put this to use: the idea was to simplify the methodology presented in the Hercules example further. While initially complicating understanding of how it all works, perhaps, the below removes all necessity for a switch...case...endSwitch construct in your 'server' library. In server.lsl, we set some global variables.
var
jsLib library
loLib logical
loTrueFalse logical
dyIncoming dynarray[] string
stReturnValue string
stPath string
arVersion array[] string
endVar
method open(var eventInfo Event)
var
arPath array[] string
endvar
loLib=False
; break apart the version of Paradox
; so the correct version of the JSI Library
; will be opened. This way one copy of this
; client library can be created, and distributed
; to multiple client versions of Paradox without special
; recoding
breakapart(version(),arVersion,".")
; get the path to this library, and use that path to
; reference a copy of server.lsl library
breakapart(getfilename(),arPath,"\\")
stPath=arPath[1]
if arPath.size()>=3 then
for x from 2 to arPath.size()-1
stPath=stPath+"\\"+arPath[x]
endfor
endif
endMethod
method jsParseRequest(var olRequest oleAuto, var olResponse oleAuto) logical
var
liOne,
liTwo longint
endVar
; retrieve the incoming field data into a Global dynarray
; so it is available to all methods/procs in this library
; and thus also available in the new copy of the library
liTwo = olRequest.NFields - 1
if liTwo<0 then
dyIncoming.empty()
dyIncoming["ErrorMessage"]="Error: No data received"
return false
endif
for liOne from 0 to liTwo
dyIncoming[olRequest.GetFieldName(liOne)]=olRequest.GetFieldByIndex(liOne)
endFor
; add some incoming request variables to the global array
dyIncoming["IpAddress"]=olRequest.ipAddress
dyIncoming["URI"]=olRequest.uri
return True
endMethod
; replacement for handlerequest; this version uses the new methodology
method handlerequest(strHandler String, var Request OleAuto, var Response OleAuto) Logical
; these vars are for handling errors in the try...onFail...endTry block below
var
siErrors,
liErrors longint
arErrors array[30] string
stClock string
endvar
; create a global dynarray from the incoming data,
; which can be used by both the original copy of the library
; and the jsLib copy of the library
; There must be a connection established to new global
; variables, since there can be no parameters passed with execMethod()
if not jsParseRequest(request,response) then
response.resultstring=dyIncoming["ErrorMessage"]
return True
endif
; if a copy of server.lsl isn't open, open it with jsLib variable
if not loLib then
jsLib.open(stPath+"\\Server")
loLib=True
endif
; set default for 'errormessage' element
; It can then just be added to, rather than
; figure out later whether it has been initialized
dyIncoming["ErrorMessage"]=""
; run execMethod based on the incoming 'handler' identified in server.db
; you *could* bypass server.db at this point. But it is just too handy and flexible to
; abandon. Especially with proxying, where you can watch for /paradox/ACTION and pass
; ACTION here as the execMethod parameter.
try
jsLib.execmethod(strHandler)
if loTrueFalse then
response.resultstring=stReturnValue
else
response.resultstring=dyIncoming["ErrorMessage"]
endif
onFail
arErrors[1]=errormessage()
liErrors=1
while errorpop()
liErrors=liErrors+1
arErrors[liErrors]=errormessage()
endwhile
stClock=string(cpuclocktime())
for siErrors from 1 to liErrors
if arErrors[siErrors].search(chr(10))=arErrors[siErrors].size() then
arErrors[siErrors]=arErrors[siErrors].substr(1,arErrors[siErrors].size()-1)
endif
if arErrors[siErrors].search(chr(13))=arErrors[siErrors].size() then
arErrors[siErrors]=arErrors[siErrors].substr(1,arErrors[siErrors].size()-1)
endif
; you can build on 'ErrorMessage' element since you create default blank value near top
; of this method
dyIncoming["ErrorMessage"]=dyIncoming["ErrorMessage"]+arErrors[siErrors]+"\n"
writeprofilestring(stPath+"\\errors.ini", stClock, string(siErrors), arErrors[siErrors])
endfor
errorclear()
sleep(1)
response.resultstring=dyIncoming["ErrorMessage"]
endTry
; empty the incoming dynarray so any
; values set during this operation won't also be in the next request
dyIncoming.empty()
return True
endMethod
; getresponse is the 'action' in the html form included in this sample.
; it is as simplistic as it gets, and is included solely so that you can see that
; execMethod(strHandler) works. strHandler is the incoming 'Action' (getresponse),
; and therefore is the method that automatically executes by calling execMethod(strHandler)
; in handlerequest, above.
method getresponse()
; a VERY simplistic use
; but shows that the connection has been
; initialize between jsRequest/jsResponse and
; the incoming request from the outside
stReturnValue=dyIncoming["ipAddress"]
; all your normal processing of this request
; would take place here
; set a global variable that is a flag indicating whether this operation was successful
loTrueFalse=True
{
; if your operation fails, you could use
dyIncoming["ErrorMessage"]="Message for user to see when operation fails"
loTrueFalse=False
}
endMethod
Inspect server.lsl for how the handlerequest method connects an internal dynArray to the incoming request data, then uses execMethod() to process the incoming request without using a switch..case..endSwitch construct. The execMethod() is based on 'strHandler', which is the ACTION set in the html file (or, rather, the handler identified in server.db). Again, I am not suggesting that this alternate is better than the version that comes with Paradox. I use that version myself. However, I will be experimenting with this to determine any potential speed enhancements (or decreases). And it does simplify your coding of this. The ACTION in the form (or one you change it to in server.db) becomes the method to execute automatically, removing the need for those switch...case...endSwitch statements. |
| < Prev | Next > |
|---|





