Histórico da Página
@annotations na linguagem TL++ possibilitou a simplificação da escrita para o desenvolvimento de API´s REST, mas ainda é possível desenvolver aplicações sem utilização deste recurso no modo tradicional, trataremos disto nesta documentação.
Inicialização de um serviço REST
Para poder utilizar aplicações REST sem o uso das @annotations, obrigatoriamente é necessário criar um serviço Http por meio de uma função fazendo uso de um objeto JSon.Veremos no exemplo abaixo como fazer isto:
User Function u_fInitService()
Local oVdrCtrl := VdrCtrl():New() as object
Local cAppPath := "/examples" as character
Local nResult := -1 as integer
Local jConfig
:= JsonObject():New() as Json
//Definição dasObtem configuraçõeso doambiente HttpServeratual
jConfig['HTTPSERVER'] Local cEnv := GetEnvServer() as character
//Definição das configurações do HttpServer
jConfig['HTTPSERVER'] := JsonObject():New()
jConfig['HTTPSERVER']['Enable'] := .T.
jConfig['HTTPSERVER']['Log'] := .F.
jConfig['HTTPSERVER']['Charset'] := "ISO-8859-1"
jConfig['HTTPSERVER']['Servers'] := {"INIT_HTTP_REST"}
jConfig['INIT_HTTP_REST'] := JsonObject():New()
jConfig['INIT_HTTP_REST']['Port'] := "9995"
jConfig['INIT_HTTP_REST']['HostName'] := "TLPP_REST_SERVER"
jConfig['INIT_HTTP_REST']['ContentTypesLocations'] := {"ContentTypesHTTP_ROOT_01"}
jConfig['INIT_HTTP_REST']['LocationsContentTypes'] := {"HTTPINIT_ROOT_01ContentTypes"}
jConfig['HTTP_ROOT_01'] := JsonObject():new()
jConfig['HTTP_ROOT_01']['Path'] := cAppPath
jConfig['HTTP_ROOT_01']['RootPath'] := "C:\tlppCore\bin\root\web"
jConfig['HTTP_ROOT_01']['DefaultPage'] := {"index.html"}
jConfig['HTTP_ROOT_01']['ThreadPool'] := "INIT_THREAD_POOL_01"
jConfig['INIT_THREAD_POOL_01'] := JsonObject():new()
jConfig['INIT_THREAD_POOL_01']['Environment'] := cEnv
jConfig['INIT_THREAD_POOL_01']['MinThreads'] := 1
jConfig['INIT_THREAD_POOL_01']['MaxThreads'] := 4
jConfig['INIT_THREAD_POOL_01']['MinFreeThreads'] := 10
jConfig['INIT_THREAD_POOL_01']['GrowthFactor'] := 1
jConfig['INIT_THREAD_POOL_01']['InactiveTimeoutAcceptTimeout'] := 3000010000
jConfig['INIT_THREAD_POOL_01']['AcceptTimeoutInactiveTimeout'] := 10000
30000
jConfig['INIT_ContentTypes_THREAD_POOL_01']['ActiveTimeout'] := 120000
jConfig['INIT_THREAD_POOL_01']['Slaves'] := JsonObject():new(){"TP_AUX_1", "TP_AUX_2"}
jConfig['INIT_ContentTypes']['htmTP_AUX_1'] := "text/html"
jConfig['INIT_ContentTypes']['html'] := "text/html"JsonObject():new()
jConfig['INITTP_AUX_ContentTypes1']['stmEnvironment'] := "text/html"cEnv
jConfig['INITTP_AUX_ContentTypes1']['tspMinThreads'] := "text/html"
2
jConfig['INITTP_AUX_ContentTypes1']['jsMaxThreads'] := "text/javascript"4
jConfig['INITTP_AUX_ContentTypes1']['jsonMinFreeThreads'] := "text/plain;charset=ISO-8859-1"0
jConfig['INITTP_AUX_ContentTypes1']['*GrowthFactor'] := "application/octet-stream"
/* -----------------------------------------------------------
Aqui é feita a chamada para a função responsável por criar o
vinculo entre URN´s e as aplicações
----------------------------------------------------------- */
jConfig['INIT_HTTP_REST']['LoadURNs']1
jConfig['TP_AUX_1']['AcceptTimeout'] := 10000
jConfig['TP_AUX_1']['InactiveTimeout'] := 30000
jConfig['TP_AUX_1']['ActiveTimeout'] := 120000
jConfig['TP_AUX_2'] : := JsonObject():new()
if !( sLoadURNs(@jConfigjConfig['INITTP_HTTPAUX_REST2']['LoadURNsEnvironment']) )
return Nil
endif
/*------------------------------------------------------------*/
nResult := oVdrCtrl:Start(jConfig)
if ( ValType(nResult) == 'N' .AND. nResult == 0 )
conout("### Servidor HTTP inicializado com sucesso!")
else
conout("### Erro ao iniciar HTTP Server - retorno [" + cValToChar(nResult) + "] - " + cValToChar(oVdrCtrl:getControlErrCode()) + " - " + oVdrCtrl:getControlErrDesc() )
endif
return nResult
OBS.: Caso já exista algum serviço REST sendo criado por meio de função utilizando-se um JSon, basta incluir a chamada da static sLoadUrn como será visto no item posterior.
Vinculando a URN à API
...
:= cEnv
jConfig['TP_AUX_2']['MinThreads'] := 2
jConfig['TP_AUX_2']['MaxThreads'] := 4
jConfig['TP_AUX_2']['MinFreeThreads'] := 0
jConfig['TP_AUX_2']['GrowthFactor'] := 1
jConfig['TP_AUX_2']['AcceptTimeout'] := 10000
jConfig['TP_AUX_2']['InactiveTimeout'] := 30000
jConfig['TP_AUX_2']['ActiveTimeout'] := 120000
jConfig['INIT_ContentTypes'] := JsonObject():new()
jConfig['INIT_ContentTypes']['htm'] := "text/html"
jConfig['INIT_ContentTypes']['html'] := "text/html"
jConfig['INIT_ContentTypes']['stm'] := "text/html"
jConfig['INIT_ContentTypes']['tsp'] := "text/html"
jConfig['INIT_ContentTypes']['js'] := "text/javascript"
jConfig['INIT_ContentTypes']['json'] := "application/json;charset=utf-8"
jConfig['INIT_ContentTypes']['*'] := "application/octet-stream"
/* -----------------------------------------------------------
Aqui é feita a chamada para a função responsável por criar o
vinculo entre URN´s e as aplicações
----------------------------------------------------------- */
jConfig['INIT_HTTP_REST']['LoadURNs'] := JsonObject():new()
if !( sLoadURNs(@jConfig['INIT_HTTP_REST']['LoadURNs']) )
return Nil
endif
/*------------------------------------------------------------*/
nResult := oVdrCtrl:Start(jConfig)
if ( ValType(nResult) == 'N' .AND. nResult == 0 )
conout("### Servidor HTTP inicializado com sucesso!")
else
conout("### Erro ao iniciar HTTP Server - ret: " + cValToChar(nResult) + " - err: " + cValToChar(oVdrCtrl:nErr) + " desc: '" + cValToChar(oVdrCtrl:cErr) + "'" )
endif
return nResult
OBS.: Caso já exista algum serviço REST sendo criado por meio de função utilizando-se um JSon, basta incluir a chamada da static sLoadUrn como será visto no item posterior.
Vinculando a URN à API
Como não está sendo utilizado o recurso das @annotations para poder desenvolver API´s REST, é preciso utilizar outra forma para efetuar o vínculo com a URN, isto é feito via função.A mesma é chamada do ponto destacado na função do item anterior, no exemplo citado está sendo feita a chamada para uma função do tipo Static, mas, nada impede que ela seja do tipo User, desde que respeitando o tipo de retorno e as regras de implementações que serão vistas no exemplo a seguir:
//Static Function responsável por criar o nó contendo o vínculo entre as URN´s e as API´s(funções) no objeto jSon que foi recebido como parametro e será utilizado no serviço REST que será executado a partir das definições do mesmo.
Static Function sLoadURNs(jEndpoints)
Local cDelPath := "/documentation/noannotation/delete"
Local cGetPath := "/documentation/noannotation/get"
Local cGetParamPath := "/documentation/noannotation/get/*"
Local cGetClassPath := "/documentation/noannotation/getclass"
Local cPatchPath := "/documentation/noannotation/patch"
Local cPostPath := "/documentation/noannotation/post"
Local cPutPath := "/documentation/noannotation/put"
if(ValType(jEndpoints) == 'U' .Or. ValType(jEndpoints) != 'J')
jEndpoints := jsonObject():New()
endIf
// Exemplo de verbo com uso de funcao
jEndpoints[cGetPath] := JsonObject():new()
jEndpoints[cGetPath]['GET'] := JsonObject():new()
jEndpoints[cGetPath]['GET']['ProgramType'] := 0
jEndpoints[cGetPath]['GET']['ClassName'] := ""
jEndpoints[cGetPath]['GET']['Function'] := "U_getExampleNoAnnotation"
jEndpoints[cGetPath]['GET']['EndPoint'] := {"documentation", "noannotation", "get"}
// Para a mesma URL (cGetPath) pode ter mais de um verbo, ja tinha GET adicionando o POST
jEndpoints[cGetPath]['POST'] := JsonObject():new()
jEndpoints[cGetPath]['POST']['ProgramType'] := 0
jEndpoints[cGetPath]['POST']['ClassName'] := ""
jEndpoints[cGetPath]['POST']['Function'] := "U_getExampleNoAnnotation"
jEndpoints[cGetPath]['POST']['EndPoint'] := {"documentation", "noannotation", "get"}
// Exemplo de path com parametro
jEndpoints[cGetParamPath] := JsonObject():new()
jEndpoints[cGetParamPath]['GET'] := JsonObject():new()
jEndpoints[cGetParamPath]['GET']['ProgramType'] := 0
jEndpoints[cGetParamPath]['GET']['ClassName'] := ""
jEndpoints[cGetParamPath]['GET']['Function'] := "U_getParamExampleNoAnnotation"
jEndpoints[cGetParamPath]['GET']['EndPoint'] := {"documentation", "noannotation", "get", ":myparam"}
// Exemplo de uso com classe ao inves de funcao
jEndpoints[cGetClassPath] := JsonObject():new()
jEndpoints[cGetClassPath]['GET'] := JsonObject():new()
// Para indicar que eh uma classe informe que o tipo de programa eh 1
jEndpoints[cGetClassPath]['GET']['ProgramType'] := 1
jEndpoints[cGetClassPath]['GET']['ClassName'] := "getClassExampleNoAnnotation"
jEndpoints[cGetClassPath]['GET']['Function'] := "method_get"
jEndpoints[cGetClassPath]['GET']['EndPoint'] := {"documentation", "noannotation", "get", "class"}
// Exemplo com verbo DELETE
jEndpoints[cDelPath] := JsonObject():new()
jEndpoints[cDelPath]['DELETE'] := JsonObject():new()
jEndpoints[cDelPath]['DELETE']['ProgramType'] := 0
jEndpoints[cDelPath]['DELETE']['ClassName'] := ""
jEndpoints[cDelPath]['DELETE']['Function'] := "U_deleteExampleNoAnnotation"
jEndpoints[cDelPath]['DELETE']['EndPoint'] := {"documentation", "noannotation", "delete"}
// Exemplo com verbo PATCH
jEndpoints[cPatchPath] := JsonObject():new()
jEndpoints[cPatchPath]['PATCH'] := JsonObject():new()
jEndpoints[cPatchPath]['PATCH']['ProgramType'] := 0
jEndpoints[cPatchPath]['PATCH']['ClassName'] := ""
jEndpoints[cPatchPath]['PATCH']['Function'] := "U_patchExampleNoAnnotation"
jEndpoints[cPatchPath]['PATCH']['EndPoint'] := {"documentation", "noannotation", "patch"}
// Exemplo com verbo POST
jEndpoints[cPostPath] := JsonObject():new()
jEndpoints[cPostPath]['POST'] := JsonObject():new()
jEndpoints[cPostPath]['POST']['ProgramType'] := 0
jEndpoints[cPostPath]['POST']['ClassName'] := ""
jEndpoints[cPostPath]['POST']['Function'] := "U_postExampleNoAnnotation"
jEndpoints[cPostPath]['POST']['EndPoint'] := {"documentation", "noannotation", "post"}
// Exemplo com verbo PUT
jEndpoints[cPutPath] := JsonObject():new()
jEndpoints[cPutPath]['PUT'] //Static Function responsável por criar o nó contendo o vínculo entre as URN´s e as API´s(funções) no objeto jSon que foi recebido como parametro e será utilizado no serviço REST que será executado a partir das definições do mesmo.
Static Function sLoadURNs(jEndpoints)
Local cDelPath := "/documentation/noannotation/delete"
Local cGetPath := "/documentation/noannotation/get"
Local cPatchPath := "/documentation/noannotation/patch"
Local cPostPath := "/documentation/noannotation/post"
Local cPutPath := "/documentation/noannotation/put"
if(ValType(jEndpoints) == 'U' .Or. ValType(jEndpoints) != 'J')
jEndpoints := jsonObject():New()
endIf
jEndpoints[cGetPath] := JsonObject():new()
jEndpoints[cGetPath[cPutPath]['PUT']['GETProgramType'] := 0
jEndpoints[cPutPath]['PUT']['ClassName'] := JsonObject():new()""
jEndpoints[cGetPathcPutPath]['GETPUT']['ClassNameFunction'] := "U_putExampleNoAnnotation"
jEndpoints[cGetPathcPutPath]['GETPUT']['FunctionEndPoint'] := "U_getExampleNoAnnotation"
jEndpoints[cGetPath]['GET']['EndPoint'] :={"documentation", "noannotation", "get"}
jEndpoints[cDelPath]['DELETE'] := JsonObject():new()
jEndpoints[cDelPath]['DELETE']['ClassName'] := ""
jEndpoints[cDelPath]['DELETE']['Function'] := "U_deleteExampleNoAnnotation"
jEndpoints[cDelPath]['DELETE']['EndPoint'] :={"documentation", "noannotation", "delete"}
jEndpoints[cPatchPath]['PATCH'] := JsonObject():new()
jEndpoints[cPatchPath]['PATCH']['ClassName'] := ""
jEndpoints[cPatchPath]['PATCH']['Function'] := "U_patchExampleNoAnnotation"
jEndpoints[cPatchPath]['PATCH']['EndPoint'] :={"documentation", "noannotation", "patch"}
jEndpoints[cPostPath]['POST'] := JsonObject():new()
jEndpoints[cPostPath]['POST']['ClassName'] := ""
jEndpoints[cPostPath]['POST']['Function'] := "U_postExampleNoAnnotation"
jEndpoints[cPostPath]['POST']['EndPoint'] :={"documentation", "noannotation", "post"}
jEndpoints[cPutPath]['PUT'] := JsonObject():new()
jEndpoints[cPutPath]['PUT']['ClassName'] := ""
jEndpoints[cPutPath]['PUT']['Function'] := "U_putExampleNoAnnotation"
jEndpoints[cPutPath]['PUT']['EndPoint'] :={"documentation", "noannotation", "put"}
Return .T. := {"documentation", "noannotation", "put"}
return .T.
function U_getExampleNoAnnotation() as logical
oRest:updateKeyHeaderResponse("Content-Type", "application/json;charset=utf-8")
return oRest:setStatusResponse(200, '{"tlpp_test":"get"}')
function U_getParamExampleNoAnnotation() as logical
Local jParam := oRest:getPathParamsRequest() as Json
Local cParam := "no_param" as character
if (ValType(jParam) == 'J')
cParam := jParam["myparam"]
if (ValType(cParam) != 'C')
cParam := "invalid_param"
endif
endif
oRest:updateKeyHeaderResponse("Content-Type", "application/json;charset=utf-8")
return oRest:setStatusResponse(200, '{"tlpp_test":"get with param: ' + cValToChar(cParam) + '"}')
function U_deleteExampleNoAnnotation() as logical
oRest:updateKeyHeaderResponse("Content-Type", "application/json;charset=utf-8")
return oRest:setStatusResponse(200, '{"tlpp_test":"delete"}')
function U_patchExampleNoAnnotation() as logical
oRest:updateKeyHeaderResponse("Content-Type", "application/json;charset=utf-8")
return oRest:setStatusResponse(200, '{"tlpp_test":"patch"}')
function U_postExampleNoAnnotation() as logical
oRest:updateKeyHeaderResponse("Content-Type", "application/json;charset=utf-8")
return oRest:setStatusResponse(200, '{"tlpp_test":"post"}')
function U_putExampleNoAnnotation() as logical
oRest:updateKeyHeaderResponse("Content-Type", "application/json;charset=utf-8")
return oRest:setStatusResponse(200, '{"tlpp_test":"put"}')
class getClassExampleNoAnnotation
public method new as object
public method method_get as logical
endclass
method new() as object class getClassExampleNoAnnotation
ConOut("### Creating class: " + "getClassExampleNoAnnotation")
return self
method method_get() as logical class getClassExampleNoAnnotation
oRest:updateKeyHeaderResponse("Content-Type", "application/json;charset=utf-8")
return oRest:setStatusResponse(200, '{"tlpp_test_class":"getClassExampleNoAnnotation"}')
OBSERVAÇÃO
As funções, os endpoints e as demais configurações