REST web service
Posted: Sat Aug 15, 2015 7:25 pm
Hi,
Recently, I tried to write a RESTful web service to communicate with a web-based application (framework node-webkit/ electron.atom.io)
How to test:
Recently, I tried to write a RESTful web service to communicate with a web-based application (framework node-webkit/ electron.atom.io)
How to test:
- Run the program
- Open your web browser
- You can type one of these URLs:
Code: Select all
DeclareModule RestAPI
Global NewMap RestParam$()
Declare.i RestRequest(HttpVerb$, RestURL$)
Declare.i RestGET(RestURL$)
Declare.i RestPUT(RestURL$)
Declare.i RestPOST(RestURL$)
Declare.i RestDELETE(RestURL$)
Declare.s DefineRestTag(Tag$, RegexPattern$="", DefaultPattern$="[^/]+")
Declare.s ExtractRestRequest(HttpRequestData$)
EndDeclareModule
Module RestAPI
EnableExplicit
Structure CustomRestRequest
Pattern$
Regex.i
Map Tag$()
EndStructure
Global NewMap CustomRestRequest.CustomRestRequest()
Global NewMap CustomTagPattern$()
Global RestRequestRegex=CreateRegularExpression(#PB_Any, "^(GET|PUT|POST|DELETE)[ ]+\S+")
Global RestRequest$
Global RestTagRegex=CreateRegularExpression(#PB_Any, "{(?<tagName>[^:/]+)(:(?<tagPattern>[^/]+))?}")
DefineRestTag("id")
Procedure.s ExtractRestRequest(httpRequest$)
ClearMap(RestParam$())
If Trim(httpRequest$) And ExamineRegularExpression(RestRequestRegex, httpRequest$) And NextRegularExpressionMatch(RestRequestRegex)
RestRequest$=RegularExpressionMatchString(RestRequestRegex)
Else
RestRequest$=""
EndIf
ProcedureReturn RestRequest$
EndProcedure
Procedure.s DefineRestTag(Tag$, RegexPattern$="", DefaultPattern$="[^/]+")
If RegexPattern$
CustomTagPattern$(Tag$)=RegexPattern$
ElseIf FindMapElement(CustomTagPattern$(), Tag$)
ProcedureReturn CustomTagPattern$()
Else
ProcedureReturn DefaultPattern$
EndIf
EndProcedure
Procedure.i RestRequest(HttpVerb$, RestURL$)
ClearMap(RestParam$())
Protected customRequest$=UCase(HttpVerb$) + " " + RestURL$
Protected *customRestRequest.CustomRestRequest
With *customRestRequest
;find existing custom REST request
If FindMapElement(CustomRestRequest(), customRequest$)
*customRestRequest=CustomRestRequest()
Else
*customRestRequest=AddMapElement(CustomRestRequest(), customRequest$)
;save custom pattern and custom tags
\Pattern$="^" + customRequest$ + "$"
If ExamineRegularExpression(RestTagRegex, \Pattern$)
While NextRegularExpressionMatch(RestTagRegex)
Protected tagName$=RegularExpressionNamedGroup(RestTagRegex, "tagName")
Protected tagPattern$=RegularExpressionNamedGroup(RestTagRegex, "tagPattern")
If tagPattern$="" : tagPattern$=DefineRestTag(tagName$) : EndIf
\Pattern$=ReplaceString(\Pattern$, RegularExpressionMatchString(RestTagRegex), "(?<" + tagName$ + ">" + tagPattern$ + ")")
\Tag$(tagName$)=tagPattern$
Wend
EndIf
;save custom regex
\Regex=CreateRegularExpression(#PB_Any, \Pattern$)
;delete custom REST request if regex is invalid
If \Regex=0
DeleteMapElement(CustomRestRequest())
*customRestRequest=0
EndIf
EndIf
;if custom REST request exists, find any matches
If *customRestRequest
If ExamineRegularExpression(\Regex, RestRequest$)
If NextRegularExpressionMatch(\Regex)
ForEach \Tag$()
tagName$=MapKey(\Tag$())
RestParam$(tagName$)=RegularExpressionNamedGroup(\Regex, tagName$)
Next
ProcedureReturn #True
EndIf
EndIf
EndIf
EndWith
EndProcedure
Procedure.i RestGET(RestURL$)
ProcedureReturn RestRequest("GET", RestURL$)
EndProcedure
Procedure.i RestPUT(RestURL$)
ProcedureReturn RestRequest("PUT", RestURL$)
EndProcedure
Procedure.i RestPOST(RestURL$)
ProcedureReturn RestRequest("POST", RestURL$)
EndProcedure
Procedure.i RestDELETE(RestURL$)
ProcedureReturn RestRequest("DELETE", RestURL$)
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
; ********************
; EXAMPLE
; ********************
UseModule RestAPI
If Not InitNetwork() : MessageRequester("Error", "Can't initialize the network !") : End : EndIf
Procedure ConsoleMessage(clientID, message$, serverID)
Protected clientIP$=IPString(GetClientIP(clientID)), clientPort=GetClientPort(clientID)
PrintN("Client " + clientID + "(" + clientIP$ + ":" + clientPort + ") " + message$ + " " + serverID)
EndProcedure
DefineRestTag("name", "[\w][^/]+")
Define port=6777, httpRequest$, *httpRequestBuffer=AllocateMemory(1024)
If OpenConsole("RESTful web service")
If CreateNetworkServer(1, port)
Repeat
Define serverEvent=NetworkServerEvent(), serverID=EventServer(), clientID=EventClient()
Select serverEvent
Case #PB_NetworkEvent_Connect
ConsoleMessage(clientID, "is CONNNECTED To your server", serverID)
Case #PB_NetworkEvent_Data
;read web request
ConsoleMessage(clientID, "sent DATA to your server", serverID)
ReceiveNetworkData(clientID, *httpRequestBuffer, MemorySize(*httpRequestBuffer))
httpRequest$=PeekS(*httpRequestBuffer, -1, #PB_UTF8)
;analyze web request
If ExtractRestRequest(httpRequest$)
If RestGET("/books")
PrintN("Get all books") ;
; TODO : write specific JSON response
ElseIf RestGET("/books/{id}")
PrintN("Get book n°" + RestParam$("id"))
; TODO : write specific JSON response ;
ElseIf RestGET("/books/{id}/pages/{number:\d{1,3}}")
PrintN("Get page n°" + RestParam$("number") + " of book n°" + RestParam$("id"))
; TODO : write specific JSON response
ElseIf RestGET("/books/search/{name}")
PrintN("Search book named '" + URLDecoder(RestParam$("name")) + "'")
; TODO : write specific JSON response
ElseIf RestPUT("/books/{id}")
PrintN("Update book n°" + RestParam$("id"))
; TODO : get book data from httpRequest$
; TODO : write specific JSON response
ElseIf RestPOST("/books")
PrintN("Insert new book")
; TODO : get book data from httpRequest$
; TODO : write specific JSON response
ElseIf RestDELETE("/books/{id}")
PrintN("Delete book n°" + RestParam$("id"))
EndIf
EndIf
;send web response
responseJSON$="HTTP/1.1 200 OK" + #LF$ +
"Content-Type : application/json" + #LF$ +
"Content-Length : 61" + #LF$ + #LF$ +
"{'Id':'33','Name':'Toto','Pages':['blabla...','gibrish....']}"
SendNetworkString(clientID, responseJSON$, #PB_UTF8)
Case #PB_NetworkEvent_Disconnect
ConsoleMessage(clientID, "is DISCONNNECTED from your server", serverID)
Break
EndSelect
Delay(50) ; Don't stole whole CPU
ForEver
CloseNetworkServer(1)
EndIf
CloseConsole()
EndIf
CompilerEndIf