├── .gitattributes ├── 01.ola_mundo.apw ├── 02.transformar_palavra.apw ├── 03.campos_por_tabela.apw ├── 04.headers.apw ├── 05.status.apw ├── 06.receber_json.apw ├── 07.tarefas.apw ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.apw linguist-language=xbase -------------------------------------------------------------------------------- /01.ola_mundo.apw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | // Um passo importante é incluir o arquivo de cabeçalho que adiciona suporte 3 | // à sintaxe que utilizaremos 4 | #include 'restful.ch' 5 | 6 | // O primeiro passo é definir o protótipo do nosso serviço REST e informar uma 7 | // descrição e um formato para serem exibidos na lista de serviços 8 | WsRestful OlaMundo Description "Hello world em AdvPL REST" Format APPLICATION_JSON 9 | // Definimos a rota /ola com despacho para o verbo GET 10 | // A descrição servirá de documentação 11 | WsMethod GET Ola Description "Retorna um JSON com uma mensagem com texto" Path "/ola" 12 | End WsRestful 13 | 14 | // Definimos a implementação do método GET Ola para o serviço REST que declaramos 15 | // acima 16 | WsMethod GET Ola WsRestful OlaMundo 17 | // Chamamos `::SetResponse( cConteudo )` para definir o texto de saída para 18 | // o consumidor da nossa API. Pode se utilizá-lo múltiplas vezes. O resultado 19 | // final será concatenado. Assim, é possível enviar dados para o cliente de 20 | // maneira parcial e "por prestações" :D 21 | ::SetResponse( '{ "mensagem": "tudo é terrível!" }' ) 22 | // Todo método deve retornar um valor lógico que informa se a operação finalizou 23 | // com sucesso 24 | Return .T. 25 | 26 | -------------------------------------------------------------------------------- /02.transformar_palavra.apw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | #include 'restful.ch' 3 | 4 | // Agora teremos um serviço que disponibilizará duas rotas: 5 | // - [GET /alta] 6 | // - [GET /baixa] 7 | // E receberá um parâmetro `palavra` via query string (`?palavra=`), ou seja, pela 8 | // URL. Um exemplo de consumo envolveria `?palavra=Terrivel` 9 | WsRestful TransformarCaixa Description "Transformar caixa" Format APPLICATION_JSON 10 | // Todo dado de entrada precisa ser definido como uma propriedade do serviço 11 | // O nome da propriedade precisa ser o mesmo do dado recebido, mas não depende 12 | // se o valor é passado em caixa alta ou baixa 13 | // As anotações `As String` e `Optional` servem a propósito de documentação 14 | WsData Palavra As String Optional 15 | 16 | // Rota GET para deixar uma string em caixa alta 17 | WsMethod GET Alta Description "Deixa a string em caixa alta" Path "/alta" 18 | // Rota GET para deixar uma stirng em caixa baixa 19 | // Nas versões anteriores do AdvPL REST, só era possível existir um método GET 20 | // e o tratamento de rotas precisava ser feito na mão. Atualmente, podemos definir 21 | // um identificador único em [WsMethod ] com um caminho [Path] 22 | // que será respeitado durante a requisição! 23 | WsMethod Get Baixa Description "Deixa a string em caixa baixa" Path "/baixa" 24 | End WsRestful 25 | 26 | // Implementação do método conversor para caixa alta 27 | // Utilizamos `QueryParam ` para definir a "injeção" do valor no 28 | // parâmetro durante a execução do método via query string 29 | // Podem existir múltiplos query parameters separados por vírgula, mas todo query 30 | // parameter precisa estar declarado no protótipo do serviço 31 | WsMethod GET Alta QueryParam Palavra WsRestful TransformarCaixa 32 | // `oResultado` será um objeto JSON que irá carregar duas propriedades, 33 | // `entrada` contendo a palavra passada e `saida` contendo a palavra 34 | // transformada 35 | Local oResultado := JsonObject():New() 36 | 37 | // Para colocarmos uma propriedade no objeto JSON, utilizamos a notação 38 | // [ ] := 39 | // Também podemos usar : := , mas isso não preservará 40 | // o nome que definirmos durante a conversão para string, deixando o nome 41 | // da propriedade sempre todo maiúsculo 42 | oResultado[ "entrada" ] := ::Palavra 43 | // E aqui, de fato, transformamos a palavra! 44 | oResultado[ "saida" ] := Upper( ::Palavra ) 45 | 46 | // Enviamos os dados para o client serializando-os 47 | // Podemos tanto utilizar a `FWJsonSerialize` quando chamar 48 | // `oResultado:ToJson()` nesse caso para termos uma string JSON, mas 49 | // FWJsonSerialize irá funcionar independentemente se o dado for um objeto 50 | // ou instância de `JsonObject` 51 | ::SetResponse( FWJsonSerialize( oResultado ) ) 52 | 53 | // É boa prática limpar os *objetos* criados quando não mais utilizados 54 | // Por boa prática, vide: não queremos estourar a memória do servidor e não 55 | // podemos confiar no garbage collector 56 | FreeObj( oResultado ) 57 | // Chuchu beleza! 58 | Return .T. 59 | 60 | // A outra rota segue os mesmos padrões da anterior, mas utiliza `Lower` 61 | // ao invés de `Upper` para transformar 62 | WsMethod GET Baixa QueryParam Palavra WsRestful TransformarCaixa 63 | Local oResultado := JsonObject():New() 64 | oResultado[ "entrada" ] := ::Palavra 65 | oResultado[ "saida" ] := Lower( ::Palavra ) 66 | 67 | ::SetResponse( FWJsonSerialize( oResultado ) ) 68 | FreeObj( oResultado ) 69 | Return .T. 70 | -------------------------------------------------------------------------------- /03.campos_por_tabela.apw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | #include 'restful.ch' 3 | 4 | // Agora teremos um serviço que disponibilizará uma rota: 5 | // - [GET /campos/{tabela}] 6 | // Onde `tabela` referencia a chave representativa de uma tabela do Protheus, 7 | // correspondendo ao `X2_CHAVE` 8 | // No exemplo anterior, recebemos os dados via query params. Agora, o fazemos 9 | // embutido no caminho (path) 10 | // Quando não encontrarmos a tabela na SX2, iremos emitir um erro 404 11 | WsRestful CamposPorTabela Description "Campos por tabela" Format APPLICATION_JSON 12 | // Assim como um query param, um path param também precisa estar definido no 13 | // protótipo do serviço 14 | WsData Tabela As String Optional 15 | 16 | WsMethod GET Campos Description "Lista os campos de uma tabela e seus nomes" Path "/campos/{tabela}" 17 | End WsRestful 18 | 19 | // Implementação do método que traz os campos por tabela 20 | WsMethod GET Campos QueryParam Tabela WsRestful CamposPorTabela 21 | // Resultado em JSON com o nome da tabela vindo de `X2_NOME` e seus campos da SX3 22 | // O slot de campos é um objeto JSON composto pelo `X3_CAMPO` da `SX3` como chave 23 | // e `X3_TITULO` como valor 24 | Local oResultado := JsonObject():New() 25 | 26 | // Abertura da SX2 e SX3. Aqui utilizamos o driver CTREECDX para abrir 27 | // diretamente do sistema de arquivos em modo compartilhado e somente leitura em 28 | // uma empresa de teste 99 29 | DbUseArea( .T., "CTREECDX", "\system\sx2990.dtc", "SX2", .T., .T. ) 30 | DbUseArea( .T., "CTREECDX", "\system\sx3990.dtc", "SX3", .T., .T. ) 31 | DbSelectArea( "SX2" ) 32 | SX2->( DbSetOrder( 1 ) ) 33 | // Tentamos posicionar na SX2 pela tabela passada 34 | If SX2->( DbSeek( Upper( ::Tabela ) ) ) 35 | oResultado[ "chave" ] := SX2->X2_CHAVE 36 | oResultado[ "nome" ] := SX2->X2_NOME 37 | oResultado[ "campos" ] := JsonObject():New() 38 | 39 | // Filtramos os campos da SX3 pertencentes à tabela 40 | DbSelectArea( "SX3" ) 41 | SX3->( DbSetFilter( { || FIELD->X3_ARQUIVO == SX2->X2_CHAVE }, "X3_ARQUIVO=SX2->X2_CHAVE" ) ) 42 | SX3->( DbGoTop() ) 43 | Do While !SX3->( Eof() ) 44 | oResultado[ "campos", SX3->X3_CAMPO ] := SX3->X3_TITULO 45 | SX3->( DbSkip() ) 46 | EndDo 47 | SX3->( DbCloseArea() ) 48 | 49 | // Enviamos os dados ao cliente 50 | ::SetResponse( FWJsonSerialize( oResultado ) ) 51 | Else 52 | // Ops, a tabela não foi encontrada! 53 | // Utilizamos a função `SetRestFault( nCodigo, cMensagem )` para definir 54 | // um estado de erro. `nCodigo` corresponde a um código HTTP válido para o 55 | // erro. No caso, representamos "não encontrado" como 404 e, ao fim, retornamos 56 | // .F. para informar que houve um erro. Deixe os erros fluirem, não se preocupe, 57 | // porque é responsabilidade do framework interceptá-los 58 | SetRestFault( 404, "Tabela não encontrada" ) 59 | Return .F. 60 | EndIf 61 | 62 | // Lembre-se, por boa prática, de fechar as áreas de trabalho abertas e preferir 63 | // a utilização de aliases ao invés de chamar as funções globalmente 64 | SX2->( DbCloseArea() ) 65 | 66 | FreeObj( oResultado ) 67 | Return .T. 68 | -------------------------------------------------------------------------------- /04.headers.apw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | #include 'restful.ch' 3 | 4 | // Exemplos com headers 5 | WsRestful Headers Description "Exemplos com headers" Format APPLICATION_JSON 6 | // Assim como path params e query params, header params são definidos como 7 | // propriedades 8 | WsData Authorization As String Optional 9 | 10 | WsMethod GET Autorizacao Description "Devolve o conteúdo do header Authorization" Path "/headers" 11 | End WsRestful 12 | 13 | // Vamos receber o conteúdo do header `Authorization` e devolver como um 14 | // header chamado `Autorizacao` 15 | WsMethod GET Autorizacao HeaderParam Authorization WsRestful Headers 16 | ::SetHeader( ::Authorization ) 17 | 18 | // Também podemos obter um header utilizando o método `::GetHeader` 19 | // Exemplo exibindo no console o navegador do cliente 20 | ConOut( ::GetHeader( "User-Agent" ) ) 21 | Return .T. 22 | -------------------------------------------------------------------------------- /05.status.apw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | #include 'restful.ch' 3 | 4 | // Exemplos com status HTTP 5 | WsRestful StatusHttp Description "Exemplos com status HTTP" Format APPLICATION_JSON 6 | WsMethod GET Status Description "Brincadeiras com alteração de status" Path "/status" 7 | End WsRestful 8 | 9 | // Ignore a ordem de execução abaixo. Preste atenção apenas no que 10 | // cada função se propõe a fazer 11 | WsMethod GET Status WsRestful StatusHttp 12 | // Código de status padrão para "tá tudo certinho aqui" e que é enviado 13 | // ao cliente se você não definir 14 | ::SetStatus( 200 ) 15 | 16 | // Quando fizer alguma validação ou tratativa, utilize `SetRestFault` 17 | // Consulte a tabela de status HTTP para mais informações 18 | // Lembre-se de retornar .F. quão cedo for possível ao definir um erro 19 | SetRestFault( 404, "Felicidade não encontrada" ) 20 | SetRestFault( 403, "Proibido, você não pode passar!" ) 21 | Return .T. 22 | -------------------------------------------------------------------------------- /06.receber_json.apw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | #include 'restful.ch' 3 | 4 | // Exemplos com status HTTP 5 | WsRestful ReceberJson Description "Exemplos com JSON na requisição" Format APPLICATION_JSON 6 | WsMethod POST Root Description "Recebe um JSON e mostra alguns dados no console" Path "/" 7 | End WsRestful 8 | 9 | WsMethod POST Root WsRestful ReceberJson 10 | // `::GetContent()` retorna o corpo da requisição em bytes 11 | Local cCorpo := ::GetContent() 12 | // Objeto que irá guardar o JSON de entrada 13 | Local oJson := JsonObject():New() 14 | 15 | // Parsing da entrada 16 | oJson:FromJson( cCorpo ) 17 | 18 | // Para fins didáticos, apenas exibimos os valores de "nome" e "idade" 19 | // no terminal 20 | ConOut( oJson[ "nome" ] ) 21 | ConOut( oJson[ "idade" ] ) 22 | 23 | // Hora de limpar a bagunça 24 | FreeObj( oJson ) 25 | Return .T. 26 | -------------------------------------------------------------------------------- /07.tarefas.apw: -------------------------------------------------------------------------------- 1 | #include 'protheus.ch' 2 | #include 'restful.ch' 3 | 4 | // Nome da tabela do sistema que utilizaremos 5 | #define TABELA_TAREFAS "tarefas" 6 | 7 | // Preparamos a base de dados para nosso CRUD 8 | // Utilizaremos o driver SQLite do sistema disponível a partir do Protheus 12 9 | // Invoque `U_TAREFAS` antes de começar a fazer as requisições para preparar a 10 | // tabela 11 | User Function Tarefas() 12 | Local aStruct := {} 13 | 14 | AAdd( aStruct, { "CONTEUDO", "C", 128, 0 } ) // Conteúdo da tarefa 15 | AAdd( aStruct, { "DT_CRIACAO", "C", 8, 0 } ) // Data que foi criada 16 | AAdd( aStruct, { "HR_CRIACAO", "C", 8, 0 } ) // Hora que foi criada 17 | AAdd( aStruct, { "DT_COMPLET", "C", 8, 0 } ) // Data que foi completada 18 | AAdd( aStruct, { "HR_COMPLET", "C", 8, 0 } ) // Hora que foi completada 19 | 20 | DbCreate( TABELA_TAREFAS, aStruct, "SQLITE_SYS" ) 21 | Return 22 | 23 | WsRestful Tarefas Description "Gerenciador de tarefas" Format APPLICATION_JSON 24 | // ID de uma tarefa. O `As Integer` já injeta o parâmetro como um numérico 25 | // do AdvPL para a gente, sem a necessidade de usar `Val`! 26 | WsData Id As Integer Optional 27 | 28 | // Métodos e suas rotas 29 | // Você pode ter quantos `WsMethod` forem necessários, desde que a chave 30 | // seja único por verbo + id. da ação 31 | // 32 | // (verbo) (Id. da ação) (descrição documentativa) (rota real da requisição) 33 | // | | |__________. | 34 | // V V V V 35 | WsMethod GET ListarTarefas Description "Listar tarefas" Path "/tarefas" 36 | WsMethod POST CriarTarefa Description "Criar tarefa" Path "/tarefa" 37 | WsMethod PUT AtualizarTarefa Description "Atualizar tarefa" Path "/tarefa/{id}" 38 | WsMethod DELETE ApagarTarefa Description "Apagar tarefa" Path "/tarefa/{id}" 39 | End WsRestful 40 | 41 | // [GET /tarefas] - listar todas as tarefas 42 | // Retorna um JSON como array de objetos com as informações de cada tarefa 43 | WsMethod GET ListarTarefas WsRestful Tarefas 44 | // Array de retorno 45 | Local aRetorno := {} 46 | // Alias de tabela SQLite 47 | Local cTarefas := GetNextAlias() 48 | Local oTarefa 49 | 50 | // Abrimos a tabela de tarefas e percorremos os registros 51 | DbUseArea( .T., "SQLITE_SYS", TABELA_TAREFAS, cTarefas, .T., .T. ) 52 | DbSelectArea( cTarefas ) 53 | Do While !( cTarefas )->( Eof() ) 54 | // Criamos um objeto JSON para cada registro 55 | // Usaremos o número do registro como ID 56 | // Devolveremos a data e hora de criação em um formato ISO padrão 57 | // Uma tarefa está completada se há registro de data que ela foi completada 58 | // Devolvemos o conteúdo sem espaços sobrando 59 | oTarefa := JsonObject():New() 60 | oTarefa[ "id" ] := ( cTarefas )->( RecNo() ) 61 | oTarefa[ "criada_em" ] := FWTimeStamp( 3, SToD( ( cTarefas )->DT_CRIACAO ), ( cTarefas )->HR_CRIACAO ) 62 | oTarefa[ "completada" ] := !Empty( ( cTarefas )->DT_COMPLET ) 63 | oTarefa[ "conteudo" ] := AllTrim( ( cTarefas )->CONTEUDO ) 64 | // Adicionamos cada objeto JSON ao retorno e vamos para o próximo registro 65 | AAdd( aRetorno, oTarefa ) 66 | ( cTarefas )->( DbSkip() ) 67 | EndDo 68 | 69 | // Fechamos a área de trabalho de tarefas e devolvemos a lista de tarefas 70 | ( cTarefas )->( DbCloseArea() ) 71 | ::SetResponse( FWJsonSerialize( aRetorno ) ) 72 | Return .T. 73 | 74 | // [POST /tarefa] - criar uma tarefa 75 | // Deve receber no corpo da requisição JSON no formato `{ "conteudo": "..." }` 76 | WsMethod POST CriarTarefa WsRestful Tarefas 77 | Local oJson := JsonObject():New() 78 | Local oRetorno 79 | Local cTarefas 80 | Local cData 81 | Local cHora 82 | 83 | // `oJson` irá carregar os dados vindos do corpo da requisição 84 | oJson:FromJson( ::GetContent() ) 85 | // Se o conteúdo não for informado, é um `BAD REQUEST` 86 | If Empty( oJson[ "conteudo" ] ) 87 | SetRestFault( 400, "É necessário informar o conteúdo" ) 88 | Return .F. 89 | EndIf 90 | 91 | // Abrimos a tabela de tarefas, computamos em strings a data e hora atuais 92 | // e gravamos o registro com bloqueio 93 | cTarefas := GetNextAlias() 94 | cData := DToS( Date() ) 95 | cHora := Time() 96 | DbUseArea( .T., "SQLITE_SYS", TABELA_TAREFAS, cTarefas ) 97 | DbSelectArea( cTarefas ) 98 | ( cTarefas )->( DbAppend( .F. ) ) 99 | ( cTarefas )->CONTEUDO := oJson[ "conteudo" ] 100 | ( cTarefas )->DT_CRIACAO := cData 101 | ( cTarefas )->HR_CRIACAO := cHora 102 | ( cTarefas )->( DbCommit() ) 103 | 104 | // É idiomático em REST retornarmos as informações do registro criado 105 | oRetorno := JsonObject():New() 106 | oRetorno[ "id" ] := ( cTarefas )->( RecNo() ) 107 | oRetorno[ "conteudo" ] := oJson[ "conteudo" ] 108 | oRetorno[ "criada_em" ] := FWTimeStamp( 3, SToD( cData ), cHora ) 109 | oRetorno[ "completada" ] := .F. 110 | 111 | // Iremos responder com um JSON com o retorno serializado em JSON 112 | // Também definimos o status HTTP como 201, isto é, `CREATED` 113 | ( cTarefas )->( DbCloseArea() ) 114 | ::SetStatus( 201 ) 115 | ::SetContentType( APPLICATION_JSON ) 116 | ::SetResponse( FWJsonSerialize( oRetorno ) ) 117 | // Hora de liberar memória 118 | FreeObj( oJson ) 119 | FreeObj( oRetorno ) 120 | Return .T. 121 | 122 | // [PUT /tarefa/{id}] - atualiza uma tarefa pelo ID 123 | // O formato do corpo da requisição é o mesmo do de criação 124 | WsMethod PUT AtualizarTarefa PathParam Id WsRestful Tarefas 125 | Local oJson := JsonObject():New() 126 | Local oRetorno 127 | Local cTarefas 128 | Local cData 129 | Local cHora 130 | 131 | // `oJson` irá carregar os dados vindos do corpo da requisição 132 | oJson:FromJson( ::GetContent() ) 133 | // Se o conteúdo não for informado, é um `BAD REQUEST` 134 | If Empty( oJson[ "conteudo" ] ) 135 | SetRestFault( 400, "É necessário informar o conteúdo" ) 136 | Return .F. 137 | EndIf 138 | 139 | // Abrimos a tabela de tarefas e atualizamos o conteúdo 140 | cTarefas := GetNextAlias() 141 | DbUseArea( .T., "SQLITE_SYS", TABELA_TAREFAS, cTarefas ) 142 | DbSelectArea( cTarefas ) 143 | ( cTarefas )->( DbRLock() ) 144 | ( cTarefas )->CONTEUDO := oJson[ "conteudo" ] 145 | ( cTarefas )->( DbCommit() ) 146 | ( cTarefas )->( DbUnlock() ) 147 | 148 | // É idiomático em REST retornarmos as informações do registro atualizado 149 | oRetorno := JsonObject():New() 150 | oRetorno[ "id" ] := ( cTarefas )->( RecNo() ) 151 | oRetorno[ "criada_em" ] := FWTimeStamp( 3, SToD( ( cTarefas )->DT_CRIACAO ), ( cTarefas )->HR_CRIACAO ) 152 | oRetorno[ "completada" ] := !Empty( ( cTarefas )->DT_COMPLET ) 153 | oRetorno[ "conteudo" ] := AllTrim( ( cTarefas )->CONTEUDO ) 154 | 155 | // Iremos responder com um JSON com o retorno serializado em JSON 156 | ( cTarefas )->( DbCloseArea() ) 157 | ::SetContentType( APPLICATION_JSON ) 158 | ::SetResponse( FWJsonSerialize( oRetorno ) ) 159 | // Hora de liberar memória 160 | FreeObj( oJson ) 161 | FreeObj( oRetorno ) 162 | Return .T. 163 | 164 | // [DELETE /tarefa/{id}] - apaga uma tarefa pelo ID 165 | // Retorna erro 404 se a tarefa não existir 166 | WsMethod DELETE ApagarTarefa PathParam Id WsRestful Tarefas 167 | Local cTarefas := GetNextAlias() 168 | 169 | // Abrimos a tabela de tarefas e tentamos posicionar no registro passado 170 | DbUseArea( .T., "SQLITE_SYS", TABELA_TAREFAS, cTarefas ) 171 | DbSelectArea( cTarefas ) 172 | ( cTarefas )->( dbGoTo( ::Id ) ) 173 | If ( cTarefas )->( Eof() ) 174 | // Fim de arquivo indica que a tarefa não existe 175 | SetRestFault( 404, "Tarefa não encontrada" ) 176 | Return .F. 177 | EndIf 178 | 179 | // Travamos o registro, deletamos e liberamos o bloqueio 180 | ( cTarefas )->( DbRLock() ) 181 | ( cTarefas )->( DbDelete() ) 182 | ( cTarefas )->( DbCloseArea() ) 183 | Return .T. 184 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Marcelo Camargo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exemplos AdvPL REST 2 | 3 | Este repositório contém um conjunto de implementações de serviços REST em AdvPL 4 | cautelosamente comentados com o objetivo de servir de referência à construção 5 | de novas aplicações e auxiliar no aprendizado das características dessa tecnologia. 6 | 7 | Os exemplos estão ordenados por complexidade e nomeados de acordo com suas 8 | características funcionais. 9 | 10 | - [Olá mundo!](./01.ola_mundo.apw) - seu primeiro webservice com apenas uma rota `GET` simples e retorno em JSON. 11 | - [Transformar palavra](./02.transformar_palavra.apw) - webservice com duas rotas `GET` para deixar palavras em caixa alta ou baixa. 12 | - [Campos por tabela](./03.campos_por_tabela.apw) - uma única rota `GET` que permite trazer os campos definidos na `SX3` para uma tabela existente na `SX2` recebendo o identificador do arquivo da tabela na própria URL. 13 | - [Headers](./04.headers.apw) - demonstração de como receber e enviar headers HTTP. 14 | - [Status](./05.status.apw) - devolvendo códigos de status HTTP. 15 | - [Receber JSON](./06.receber_json.apw) - recebendo JSON como corpo de uma requisição via `POST`. 16 | - [Tarefas](./07.tarefas.apw) - cadastro completo de lista de tarefas contemplando `GET`, `POST`, 17 | `PUT` e `DELETE` com múltiplos caminhos e validações. 18 | - [Download de arquivo](./07.download_de_arquivo.apw) - `TODO` - demonstração de como podemos forçar o download de 19 | um arquivo do servidor e customizar suas propriedades. 20 | - [Renderizar imagem](./08.renderizar_imagem.apw) - `TODO` - utilizando a mesma técnica do download de arquivo, permitimos 21 | renderização de imagens através de rotas específicas. 22 | - [Upload de imagem](./09.upload_de_imagem.apw) - `TODO` - permitimos que nosso servidor seja capaz de receber 23 | arquivos de imagem do cliente. 24 | - [Cookies](./10.cookies.apw) - `TODO` - utilizando cookies para gerenciar sessões dos usuários. 25 | --------------------------------------------------------------------------------