REST web service

Share your advanced PureBasic knowledge/code with the community.
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

REST web service

Post by eddy »

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:
  1. Run the program
  2. Open your web browser
  3. 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
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Re: REST web service

Post by eddy »

Update
- I fixed port of sample URLs
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: REST web service

Post by Kwai chang caine »

Tested on XP SP3
Works fine, thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply