Via JSON (código fonte)

Além do clássico método via INI, o REST server disponibiliza o recurso de subir um serviço REST através de execução dinâmica. Ou seja: após a subida completa do AppServer, é possível indicar a inicialização de novos serviços REST sob demanda, a hora que desejar.
Esse modo é feito através de código-fonte (em TLPP) e, no processo de inicialização, é preciso informar a configuração desejada (a mesma estrutura vista no arquivo appserver.ini) através de um objeto JSON.

Inicialização de um serviço REST dinamicamente
Segue um exemplo abrangente mapeando as opções de Server, ThreadPool e endpoints manuais.

#include "tlpp-core.th"

/*

Uso:   rest.u_start
*/

namespace rest

Function u_start()
Local oVdrCtrl            := Nil
Local nResult             := 0
Local jo                  := Nil
Local cEnv                := ""
Local cURLTest            := ""
Local cURLTest2           := ""
Local cURLTest3           := ""
Local cGetWithoutAnnot    := "test_without_annotation"
Local cGetPath            := "/" + cGetWithoutAnnot

// Sessoes
Local sSS_HTTPSERVER      := "HTTPSERVER"
Local sSS_ServerName      := "TEST_REST_SERVER"
Local sSS_Location        := "TEST_REST_LOCATION"
Local sSS_ThreadPool      := "TEST_REST_THREADPOOL"
Local sSS_ContentTypes    := "TEST_REST_CONTENTTYPES"
Local sSS_Slave01         := "TEST_REST_SLAVE01"
Local sSS_CORS            := ""

Local sSS_UserExits       := ""
Local sSS_UserExitsSlave  := ""

// Variaveis
Local cAppPath            := "/rest"
Local cAppRootPath        := "C:\tlppCore\bin\root\web"
Local nAppPort            := 10002

Local cAppSSL_Certificate := ""
Local cAppSSL_key         := ""
Local cAppSSL_passphrase  := ""
Local cAppSSL_method      := "SSL/TLS"

Local cOnStartFunc        := "rest.u_main_on_start"
Local cOnStopFunc         := ""
Local cOnSelectFunc       := "rest.u_main_on_select"
Local cOnErrorFunc        := ""

Local cOnStartFuncSlave   := "rest.u_slave_on_start"
Local cOnStopFuncSlave    := ""
Local cOnSelectFuncSlave  := "rest.u_slave_on_select"
Local cOnErrorFuncSlave   := ""

// Habilita o uso das funcoes locais de preparacao de ambientes e selecao de thread pool slaves e/ou tratamento da mensagem
// sSS_UserExits             := "TEST_REST_UE"
// sSS_UserExitsSlave        := "TEST_REST_UE_SLV"

// Obtem o environment
cEnv := GetEnvServer()

//------------------------------------------------------------------------------
// Cria o objeto de start do Server
oVdrCtrl := VdrCtrl():New()

// Cria o objeto de json com as informacoes do servico
jo := JsonObject():new()

jo[sSS_HTTPSERVER] := JsonObject():new()

jo[sSS_HTTPSERVER]['Log'] := .F.
jo[sSS_HTTPSERVER]['Servers'] := {sSS_ServerName}

jo[sSS_ServerName] := JsonObject():new()
jo[sSS_ServerName]['HostName'] := "Meu servidor rest"
jo[sSS_ServerName]['Port'] := nAppPort
jo[sSS_ServerName]['Charset'] := "UTF8"
jo[sSS_ServerName]['Locations'] := {sSS_Location}

If(!Empty(cAppSSL_Certificate))
jo[sSS_ServerName]['SslCertificate'] := cAppSSL_Certificate

If(!Empty(cAppSSL_key))
  jo[sSS_ServerName]['SslCertificateKey'] := cAppSSL_key
EndIf
If(!Empty(cAppSSL_passphrase))
  jo[sSS_ServerName]['SslCertificatePass'] := cAppSSL_passphrase
EndIf
If(!Empty(cAppSSL_method))
  jo[sSS_ServerName]['SslMethod'] := cAppSSL_method
EndIf

cURLTest := "https://"


Else
cURLTest := "http://"
EndIf

If(!Empty(sSS_ContentTypes))
jo[sSS_ServerName]['ContentTypes'] := sSS_ContentTypes
EndIf

jo[sSS_Location] := JsonObject():new()
jo[sSS_Location]['Path'] := cAppPath
jo[sSS_Location]['RootPath'] := cAppRootPath
jo[sSS_Location]['DefaultPage'] := {"index.html"}
jo[sSS_Location]['ThreadPool'] := sSS_ThreadPool

jo[sSS_ThreadPool] := JsonObject():new()
jo[sSS_ThreadPool]['Environment'] := cEnv
jo[sSS_ThreadPool]['MinThreads'] := 1
jo[sSS_ThreadPool]['MaxThreads'] := 5
jo[sSS_ThreadPool]['GrowthFactor'] := 1
jo[sSS_ThreadPool]['MinFreeThreads'] := 1
jo[sSS_ThreadPool]['InactiveTimeout'] := 180000 // 180 segundos
jo[sSS_ThreadPool]['AcceptTimeout'] := 5000 // 5 segundos

If(!Empty(sSS_UserExits))
jo[sSS_ThreadPool]['UserExits'] := sSS_UserExits

jo[sSS_UserExits] := JsonObject():new()

If(!Empty(cOnStartFunc))
  jo[sSS_UserExits]['OnStart'] := cOnStartFunc
EndIf
If(!Empty(cOnStopFunc))
  jo[sSS_UserExits]['OnStop'] := cOnStopFunc
EndIf
If(!Empty(cOnSelectFunc))
  jo[sSS_UserExits]['OnSelect'] := cOnSelectFunc
EndIf
If(!Empty(cOnErrorFunc))
  jo[sSS_UserExits]['OnError'] := cOnErrorFunc
EndIf


EndIf

If(!Empty(sSS_Slave01))
jo[sSS_ThreadPool]['Slaves'] := {sSS_Slave01}

jo[sSS_Slave01] := JsonObject():new()
jo[sSS_Slave01]['Environment'] := cEnv
jo[sSS_Slave01]['MinThreads'] := 1
jo[sSS_Slave01]['MaxThreads'] := 5
jo[sSS_Slave01]['MinFreeThreads'] := 1
jo[sSS_Slave01]['GrowthFactor'] := 1

If(!Empty(sSS_UserExitsSlave))
  jo[sSS_Slave01]['UserExits'] := sSS_UserExitsSlave

  jo[sSS_UserExitsSlave] := JsonObject():new()

  If(!Empty(cOnStartFuncSlave))
    jo[sSS_UserExitsSlave]['OnStart'] := cOnStartFuncSlave
  EndIf
  If(!Empty(cOnStopFuncSlave))
    jo[sSS_UserExitsSlave]['OnStop'] := cOnStopFuncSlave
  EndIf
  If(!Empty(cOnSelectFuncSlave))
    jo[sSS_UserExitsSlave]['OnSelect'] := cOnSelectFuncSlave
  EndIf
  If(!Empty(cOnErrorFuncSlave))
    jo[sSS_UserExitsSlave]['OnError'] := cOnErrorFuncSlave
  EndIf
EndIf


EndIf

If(!Empty(sSS_ContentTypes))
jo[sSS_ContentTypes] := JsonObject():new()

jo[sSS_ContentTypes]['htm']   := "text/html"
jo[sSS_ContentTypes]['html']  := "text/html"
jo[sSS_ContentTypes]['json']  := "application/json"
jo[sSS_ContentTypes]['js']    := "application/javascript"
jo[sSS_ContentTypes]['txt']   := "text/plain"
jo[sSS_ContentTypes]['*']     := "application/octet-stream"


EndIf

If (!Empty(sSS_CORS))
jo[sSS_Location]['CORS'] := {sSS_CORS}

jo[sSS_CORS] := JsonObject():new()
jo[sSS_CORS]['AllowOrigins']  := {"http://127.0.0.1:8080","http://localhost:8080"}
jo[sSS_CORS]['AllowMethods'] := {"POST", "GET", "PUT", "DELETE", "PATCH"}


EndIf

// Criar uma URN diretamente (sem annotation)
If (!Empty(cGetWithoutAnnot))
jEndpoints := JsonObject():new()
jo[sSS_ServerName]['LoadURNs']                := jEndpoints

jEndpoints[cGetPath] := JsonObject():new()

jEndpoints[cGetPath]['GET']                   := JsonObject():new()
jEndpoints[cGetPath]['GET']['ClassName']      := ""
jEndpoints[cGetPath]['GET']['Function']       := "rest.u_getWithoutAnnotation"
jEndpoints[cGetPath]['GET']['EndPoint']       := {cGetWithoutAnnot, "get"}


EndIf

ConOut("Starting rest server on port: " + cValToChar(jo[sSS_ServerName]['Port']))
nResult := oVdrCtrl:Start(jo)

If(ValType(nResult) == 'N' .AND. nResult == 0)
ConOut("Rest server started")

cURLTest  += "localhost:"
cURLTest  += cValToChar(jo[sSS_ServerName]['Port'])
cURLTest  += jo[sSS_Location]['Path']
cURLTest2 := cURLTest
cURLTest3 := cURLTest
cURLTest  += "/tlpp/environment"
cURLTest2 += "/test"
cURLTest3 += cGetPath

ConOut("To test try on browser: ")
ConOut(cURLTest)
ConOut(cURLTest2)
ConOut(cURLTest3)
ConOut("")

Return .T.


Else
// Falhou
ConOut("Fail to start rest server [" + cValToChar(nResult) + "] - " + cValToChar(oVdrCtrl:nErr) + " - " + cValToChar(oVdrCtrl:cErr))
Return .F.
EndIf
Return .F.

@Get("/test")
Function u_getWithAnnotation()
oRest:setResponse(time() + " " + "Hello TLPP World")
Return .T.

Function u_getWithoutAnnotation()
oRest:setResponse(time() + " " + "Hello TLPP World - Without Annotation")
Return .T.

Function u_main_on_start()
ConOut(cValToChar(ThreadId()) + " MAIN:  " + ProcName())
Return .T.

Function u_main_on_select()
ConOut(cValToChar(ThreadId()) + " MAIN:  " + ProcName())
Return -1

Function u_slave_on_start()
ConOut(cValToChar(ThreadId()) + " SLAVE: " + ProcName())
Return .T.

Function u_slave_on_select()
ConOut(cValToChar(ThreadId()) + " SLAVE: " + ProcName())
Return 0

 Para saber mais sobre a carga dinâmica de rotas via a chave LoadURNs, acessando as URNs diretamente e criando serviços, veja a documentação de REST sem uso de annotation.