finalização endpoints_2
This commit is contained in:
parent
ac4298bde8
commit
7fa89fd073
Binary file not shown.
|
After Width: | Height: | Size: 280 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 236 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 246 KiB |
@ -1,5 +1,9 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import ImgVscRestClient from "../../assets/img/44_vsc_rest_client.png";
|
||||||
|
import ImgVscRestClientArquivo from "../../assets/img/45_vsc_rest_client_arquivo.png";
|
||||||
|
import ImgVscRestClientGet from "../../assets/img/46_vsc_rest_client_get.png";
|
||||||
|
import ImgVscRestClientPost from "../../assets/img/47_vsc_rest_client_post.png";
|
||||||
|
import ImgVscRestClientPut from "../../assets/img/48_vsc_rest_client_put.png";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@ -54,8 +58,14 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Se você precisasse desenvolver um sistema desktop, como você o faria? Para criar uma janela no windows, você precisaria utilizar as APIs do <a href="https://learn.microsoft.com/pt-br/windows/win32/desktop-programming" target="_blank">Win32</a>.
|
Se você precisasse desenvolver um sistema desktop, como você o faria? Para criar uma janela no sistema operacional Windows, você precisaria utilizar as APIs do <a href="https://learn.microsoft.com/pt-br/windows/win32/desktop-programming" target="_blank">Win32</a>.
|
||||||
No linux, há o <a href="https://www.x.org/wiki/Documentation/" target="_blank">X.Org</a> e o <a href="https://wayland.freedesktop.org/docs/html/" target="_blank">Wayland</a>. Você gostaria que sua aplicação fosse multiplataforma, mas para isso você teria que escrever códigos diferentes que utilizariam cada API de cada sistema operacional. Você poderia pesquisar uma biblioteca ou framework que facilita esse processo, abstraindo os detalhes internos de cada API nativa dos sistemas operacionais em uma única API, como o <a href="https://www.qt.io/">Qt</a>. Seu progama, então, utiliza este framework para mostrar uma interface, que por sua vez, internamente, utiliza as APIs nativas do sistema operacional onde o seu software está sendo executado. Já os sistemas operacionais utilizam as APIs do seu driver de vídeo para exibir as janelas no seu monitor, do controlador USB para ler o teclado e mouse, entre outros. Ou seja, com APIs podemos criar várias camadas de abstração. Todo o ambiente em um computador é composto por softwares comunicando-se uns com os outros, utilizando estas abstrações, através de APIs.
|
No Linux, para fazer a mesma coisa, você precisaria utilizar as APIs há o <a href="https://www.x.org/wiki/Documentation/" target="_blank">X.Org</a> ou do <a href="https://wayland.freedesktop.org/docs/html/" target="_blank">Wayland</a>.
|
||||||
|
Naturalmente, cada sistema operacional diferente possui APIs diferentes para criar interfaces gráficas.
|
||||||
|
Porém, você gostaria que sua aplicação fosse multiplataforma, mas para que isso aconteça, você teria que escrever códigos diferentes que utilizariam cada API de cada sistema operacional.
|
||||||
|
Você poderia pesquisar uma biblioteca ou framework que facilita esse processo, abstraindo os detalhes internos de cada API nativa dos sistemas operacionais em uma única API.
|
||||||
|
Seu progama, então, utiliza este framework para mostrar uma interface, que por sua vez, internamente, utiliza as APIs nativas do sistema operacional onde o seu software está sendo executado.
|
||||||
|
Já os sistemas operacionais por sua vez utilizam as APIs do seu driver de vídeo para exibir a interface no seu monitor, as APIs do controlador USB para ler eventos de teclado e mouse, entre outros.
|
||||||
|
Ou seja, com APIs podemos criar várias camadas de abstração. Todo o ambiente em um computador é composto por softwares comunicando-se uns com os outros, utilizando estas abstrações, através de APIs.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@ -150,4 +160,365 @@ Response:
|
|||||||
"titulo": "Contos da Palma da Mão",
|
"titulo": "Contos da Palma da Mão",
|
||||||
"autor": "Yasunari Kawabata"
|
"autor": "Yasunari Kawabata"
|
||||||
}`}</code></pre>
|
}`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
E se quiséssemos cadastrar um livro, como poderíamos definir um endpoint que realizaria esta ação?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`Request:
|
||||||
|
POST /livros
|
||||||
|
|
||||||
|
{
|
||||||
|
"isbn": "9780262510875",
|
||||||
|
"titulo": "Structure and Interpretation of Computer Programs",
|
||||||
|
"autor": "Gerald Jay Sussman"
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
201 Created
|
||||||
|
{
|
||||||
|
"isbn": "9780262510875",
|
||||||
|
"titulo": "Structure and Interpretation of Computer Programs",
|
||||||
|
"autor": "Gerald Jay Sussman"
|
||||||
|
}`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
O verbo <code>POST</code> em uma requisição indica que desejamos <em>inserir</em> um recurso. No nosso caso, <code>POST /livros</code> indica que queremos criar um novo registro de livro.
|
||||||
|
Note que enviamos na requisição os dados do livro que desejamos inserir.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Se quisermos agora <em>editar</em> este mesmo livro que acabamos de inserir, podemos utilizar uma requisição com verbo <code>PUT</code>:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`Request:
|
||||||
|
PUT /livros/9780262510875
|
||||||
|
|
||||||
|
{
|
||||||
|
"isbn": "9780262510875",
|
||||||
|
"titulo": "Structure and Interpretation of Computer Programs, 2nd ed.",
|
||||||
|
"autor": "Harold Abelson, Gerald Jay Sussman, Julie Sussman"
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
200 Ok
|
||||||
|
{
|
||||||
|
"isbn": "9780262510875",
|
||||||
|
"titulo": "Structure and Interpretation of Computer Programs, 2nd ed.",
|
||||||
|
"autor": "Harold Abelson, Gerald Jay Sussman, Julie Sussman"
|
||||||
|
}`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Assim como no endpoint que obtém os dados de um livro, este também recebe na url o dado que identifica o registro, para indicar qual livro queremos editar.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Comumente utiliza-se o verbo <code>POST</code> para <em>criar</em>, e o verbo <code>PUT</code> para <em>editar</em>. Uma diferença conceitual entre os dois verbos é que o <code>POST</code> é utilizado em operações que <em>não são idempotentes</em>.
|
||||||
|
Idempotência é uma propriedade de uma operação que define que a aplicação sucessiva da mesma operação <em>não</em> altera o estado do sistema. Por exemplo, se você mandar a mesma requisição <code>PUT</code> para <em>editar</em> um livro várias vezes seguidas,
|
||||||
|
com exatamente os mesmos dados, o estado daquele registro de livro manterá-se exatamente igual.
|
||||||
|
Já no <code>POST</code>, enviar várias requisições idênticas, com os mesmos dados, geraria vários registros de livros diferentes, pois cada requisição representa uma <em>inserção</em> (ou então, um erro de validação de dados seria retornado, por exemplo, informando que já existe
|
||||||
|
um livro com o mesmo ISBN cadastrado).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Por fim, para remover um livro, podemos utilizar o verbo <code>DELETE</code>:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`Request:
|
||||||
|
DELETE /livros/9788574481371
|
||||||
|
|
||||||
|
Response:
|
||||||
|
204 No Content`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Este endpoint também recebe o dado que identifica o livro, assim definimos qual registro queremos remover. Normalmente, endpoints com verbo <code>DELETE</code> não possuem corpo na requisição, e têm respostas vazias.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Definindo os endpoints na API</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Temos então que os endpoints que precisamos implementar são:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><code>GET /livros</code> para obter todos os livros no sistema;</li>
|
||||||
|
<li><code>GET /livros/{`{isbn}`}</code> para obter um único livro, identificado pelo seu ISBN;</li>
|
||||||
|
<li><code>POST /livros</code> para inserir um novo livro;</li>
|
||||||
|
<li><code>PUT /livros/{`{isbn}`}</code> para editar um livro;</li>
|
||||||
|
<li><code>DELETE /livros/{`{isbn}`}</code> para remover um livro.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Vamos traduzir esta especificação para o código. Abra seu projeto, no arquivo <code>Program.cs</code>. No exemplo anterior, deixamos este arquivo assim:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.MapGet("/", () =>
|
||||||
|
{
|
||||||
|
string[] saudacoes = ["Olá!", "こんにちは", "Привет", "Ողջույն"];
|
||||||
|
|
||||||
|
return saudacoes[Random.Shared.Next(saudacoes.Length)];
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run();`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Definimos um endpoint <code>GET /</code> que responde com uma mensagem aleatória. Vamos utilizar a mesma sintaxe para definir os outros endpoints. Mas antes, vamos criar, neste mesmo arquivo, uma classe que irá representar um livro.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.MapGet("/", () =>
|
||||||
|
{
|
||||||
|
string[] saudacoes = ["Olá!", "こんにちは", "Привет", "Ողջույն"];
|
||||||
|
|
||||||
|
return saudacoes[Random.Shared.Next(saudacoes.Length)];
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
public class Livro
|
||||||
|
{
|
||||||
|
public string? Isbn { get; set; }
|
||||||
|
public string? Titulo { get; set; }
|
||||||
|
public string? Autor { get; set; }
|
||||||
|
};`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Adicione esta classe no fim do arquivo. Todos os campos são do tipo <code>string?</code>. O <code>?</code> após o tipo determina a <em>nulabilidade</em> daquela propriedade - ou seja, determina se a propriedade pode receber valores nulos.
|
||||||
|
Desta forma, o compilador consegue te auxiliar gerando avisos quando você faz uma referência a uma variável que pode ser nula sem antes verificá-la, o que, se acontecesse, geraria um erro em tempo de execução.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Agora, abaixo do <code>MapGet("/", ...)</code>, escreva:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`app.MapGet("/", () =>
|
||||||
|
{
|
||||||
|
...
|
||||||
|
});
|
||||||
|
|
||||||
|
// Obtém uma lista com os livros registrados.
|
||||||
|
app.MapGet("/livros", () =>
|
||||||
|
{
|
||||||
|
return new Livro[]
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Isbn = "9780262510875",
|
||||||
|
Titulo = "Structure and Interpretation of Computer Programs",
|
||||||
|
Autor = "Gerald Jay Sussman"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Isbn = "9780131103627",
|
||||||
|
Titulo = "C Programming Language: ANSI C Version",
|
||||||
|
Autor = "Dennis Ritchie, Brian Kerningham"
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Isbn = "9780134190440",
|
||||||
|
Titulo = "The Go Programming Language",
|
||||||
|
Autor = "Brian Kerningham"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
public class Livro
|
||||||
|
{
|
||||||
|
...`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Esta nova chamada para <code>MapGet</code> cria um endpoint <code>GET /livros</code> que responde com um <em>array</em> fixo de livros. Vamos utilizar
|
||||||
|
este array apenas para testarmos a chamada deste endpoint.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h3>Testando o endpoint</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Na estrutura de arquivos do seu projeto, deve haver um arquivo chamado <code>Biblioteca/Biblioteca.http</code>. Se não houver, crie-o. Em seguida,
|
||||||
|
substitua o conteúdo deste arquivo pelo seguinte:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`@url = http://localhost:5000
|
||||||
|
|
||||||
|
### Obtém uma lista de livros
|
||||||
|
|
||||||
|
GET {{url}}/livros
|
||||||
|
Accept: application/json`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Em seguida, instale a extensão <a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client" target="_blank">REST Client</a> no seu VSCode.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<img src="{ImgVscRestClient}" alt="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Agora, volte em <code>Biblioteca/Biblioteca.http</code>, note que algumas opções adicionais estão sendo exibidas pelo VSCode.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<img src="{ImgVscRestClientArquivo}" alt="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Agora deve aparecer uma opção <em>Send Request</em> acima da linha <code>GET {`{{url}}`}/livros</code>. Execute o seu projeto, em seguida volte neste arquivo e clique nesta opção. Se tudo der certo,
|
||||||
|
uma requisição <code>GET /livros</code> será enviada para sua aplicação, e a resposta será exibida no lado direito da IDE.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<img src="{ImgVscRestClientGet}" alt="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Volte para o <code>Program.cs</code>. Vamos criar um <code>POST</code> agora:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`// Cria um novo livro.
|
||||||
|
app.MapPost("/livros", (Livro livro) =>
|
||||||
|
{
|
||||||
|
return livro;
|
||||||
|
});`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<code>MapPost</code> cria um novo endpoint que responde ao verbo POST. Tanto no <code>MapGet</code> quanto no <code>MapPost</code>, o segundo argumento passado para o método é uma <em>função anônima</em> (ou <em>função lambda</em>).
|
||||||
|
Uma função anônima é, basicamente, uma função que não possui nome, e que pode ser referenciada em uma variável, passada como parâmetro para outras funções, e invocada desta forma.
|
||||||
|
No caso destes métodos de mapeamento de endpoints, esta função anônima passada é a função que será executada quando o endpoint for solicitado. Os argumentos dessa função são as entradas da requisição (parâmetros de URL, corpo da requisição),
|
||||||
|
e o valor de retorno é a resposta que deve ser devolvida ao cliente.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
No caso deste <code>MapPost</code> que acabamos de escrever, note que há um argumento do tipo <code>Livro</code>. Isto indica para o framework que nosso endpoint deve receber dados que podem ser estruturados em um objeto
|
||||||
|
do tipo <code>Livro</code>. Como estamos apenas testando, nosso endpoint não fará nada além de retornar o mesmo livro que recebemos como entrada.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Execute o projeto (se já estiver executando, reinicie-o), e altere o seu <code>Biblioteca.http</code>:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`@url = http://localhost:5000
|
||||||
|
|
||||||
|
### Obtém uma lista de livros
|
||||||
|
|
||||||
|
GET {{url}}/livros
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
### Cria um novo livro
|
||||||
|
|
||||||
|
POST {{url}}/livros
|
||||||
|
Accept: application/json
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"isbn": "9780321741769",
|
||||||
|
"titulo": "The C# Programming Language",
|
||||||
|
"autor": "Anders Hejlsberg"
|
||||||
|
}`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Adicionamos uma nova linha <code>POST {`{{url}}/livros`}</code>. Cada linha abaixo desta representa um cabeçalho, o <code>Accept</code> e o <code>Content-Type</code>.
|
||||||
|
O <code>Content-Type</code> indica para o servidor o tipo de recurso que estamos enviando. Como estamos enviando um JSON, o Content-Type deve ser <code>application/json</code>
|
||||||
|
(valores de Content-Type são <a href="https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Guides/MIME_types" target="_blank">mimetypes</a>).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Logo abaixo dos cabeçalhos, definimos o corpo da requisição que vamos enviar. Cada campo deste JSON é representado por um campo da classe <code>Livro</code> no nosso código.
|
||||||
|
O framework irá realizar a leitura deste JSON e a sua conversão em um objeto automaticamente.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Agora, experimente executar esta requisição, e observe a saída.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<img src="{ImgVscRestClientPost}" alt="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Para criar um <code>PUT</code>, a sintaxe é a mesma, porém utilizando <code>MapPut</code>:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`// Edita um livro.
|
||||||
|
app.MapPut("/livros/{isbn}", (string isbn, Livro livro) =>
|
||||||
|
{
|
||||||
|
return new { editando = isbn, dados = livro };
|
||||||
|
});`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
A url que definimos agora possui um <em>parâmetro</em> definido por <code>"{`{isbn}`}"</code>. Isto significa que este pedaço da
|
||||||
|
url delimitado por chaves pode assumir qualquer valor, por exemplo, <code>PUT /livros/A</code>, <code>PUT /livros/B</code>, etc.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Reinicie o projeto. Assim como no <code>POST</code> e no <code>GET</code>, altere o <code>Biblioteca.http</code> e teste a execução do endpoint:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`### Edita um livro
|
||||||
|
|
||||||
|
PUT {{url}}/livros/9780321741769
|
||||||
|
Accept: application/json
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"isbn": "9780321741769",
|
||||||
|
"titulo": "The C# Programming Language",
|
||||||
|
"autor": "Anders Hejlsberg, Mads Torgensen"
|
||||||
|
}`}</code></pre>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<img src="{ImgVscRestClientPut}" alt="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Agora, só falta o <code>GET /livros/{`{isbn}`}</code> e o <code>DELETE /livros/{`{isbn}`}</code>:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`// Obtém os dados de um livro individual.
|
||||||
|
app.MapGet("/livros/{isbn}", (string isbn) =>
|
||||||
|
{
|
||||||
|
return new Livro() { Isbn = isbn };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove um livro.
|
||||||
|
app.MapDelete("/livros/{isbn}", (string isbn) =>
|
||||||
|
{
|
||||||
|
return Results.NoContent();
|
||||||
|
});`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Reinicie o projeto, altere o <code>Biblioteca.http</code>, e teste estes endpoints:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><code>{`### Obtém um livro individual
|
||||||
|
|
||||||
|
GET {{url}}/livros/9780321741769
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
### Remove um livro
|
||||||
|
|
||||||
|
DELETE {{url}}/livros/9780321741769
|
||||||
|
Accept: application/json`}</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Certifique-se de que você consegue executar todos estes endpoints sem nenhum erro.
|
||||||
|
Nas próximas etapas, iremos fazer a conexão do projeto com um banco de dados para que possamos implementar de fato os endpoints,
|
||||||
|
inserindo, modificando e consultando os dados.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Se estiver com dificuldades, o código fonte do projeto até agora pode ser baixado <a href="https://git.gbrl.dev/Gabriel/Biblioteca/archive/etapa2.zip">aqui</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
Loading…
x
Reference in New Issue
Block a user