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:
...
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
//
...
...
Obtem
...
o
...
ambiente
...
atual
...
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']['
...
Locations'] := {"
...
HTTP_ROOT_01"}
jConfig['INIT_HTTP_REST']['
...
ContentTypes']
...
...
:=
...
"
...
INIT_
...
ContentTypes"
...
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'] :=
...
0
jConfig['INIT_THREAD_POOL_01']['GrowthFactor'] := 1
jConfig['INIT_THREAD_POOL_01']['
...
AcceptTimeout'] :=
...
10000
jConfig['INIT_THREAD_POOL_01']['
...
InactiveTimeout']
...
:=
...
30000
jConfig['INIT
...
_THREAD_POOL_01']['ActiveTimeout']
...
:=
...
120000
...
jConfig['INIT_THREAD_POOL_01']['Slaves'] :=
...
{"TP_AUX_1", "TP_AUX_2"}
jConfig['
...
TP_AUX_1']
...
...
:=
...
JsonObject():new()
jConfig['
...
TP_AUX_
...
1']['
...
Environment'] :=
...
cEnv
jConfig['
...
TP_AUX_
...
1']['
...
MinThreads'] :=
...
2
jConfig['
...
TP_AUX_
...
1']['
...
MaxThreads'] :=
...
4
jConfig['
...
TP_AUX_
...
1']['
...
MinFreeThreads']
...
:=
...
0
jConfig['
...
TP_AUX_
...
1']['
...
GrowthFactor']
...
:=
...
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()
...
jConfig['
...
TP_
...
AUX_
...
2']['
...
Environment']
...
...
...
...
...
...
...
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/get/class"
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'] := JsonObject():new()
jEndpoints[
...
cPutPath]['PUT']['ProgramType'] := 0
jEndpoints[cPutPath]['
...
PUT']['ClassName'] := ""
jEndpoints[
...
cPutPath]['
...
PUT']['Function'] := "U_
...
putExampleNoAnnotation"
jEndpoints[
...
cPutPath]['
...
PUT']['EndPoint'
...
] := {"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
Incluir Página | ||||
---|---|---|---|---|
|