atualização bd

This commit is contained in:
Gabriel Almeida Bueno 2025-05-08 08:42:32 -03:00
parent 02531f0861
commit a12908efa6
4 changed files with 121 additions and 115 deletions

View File

@ -14,14 +14,16 @@ code {
} }
code:not(pre *) { code:not(pre *) {
font-size: .8em;
background-color: var(--color-code-bg); background-color: var(--color-code-bg);
color: var(--color-code-fg); color: var(--color-code-fg);
padding: 3px 6px; padding: 2px 5px;
border: 1px solid var(--color-code-border); border: 1px solid var(--color-code-border);
border-radius: 6px; border-radius: 6px;
} }
pre:has(code) { pre:has(code) {
font-size: .9em;
background-color: var(--color-codeblock-bg); background-color: var(--color-codeblock-bg);
color: var(--color-codeblock-fg); color: var(--color-codeblock-fg);
padding: 16px; padding: 16px;

View File

@ -20,7 +20,6 @@ h2 {
code { code {
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 16px;
} }
em { em {

View File

@ -58,7 +58,8 @@ dotnet add package MySqlConnector`}</code></pre>
O banco de dados que vamos utilizar possui uma tabela com o nome <code>Livro</code>. Esta tabela possui os seguintes campos: O banco de dados que vamos utilizar possui uma tabela com o nome <code>Livro</code>. Esta tabela possui os seguintes campos:
</p> </p>
<pre><code>{`Isbn VARCHAR(255) PRIMARY KEY NOT NULL, <pre><code>{`Id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
Isbn VARCHAR(255) NOT NULL,
Titulo VARCHAR(512) NOT NULL, Titulo VARCHAR(512) NOT NULL,
Autor TEXT NOT NULL, Autor TEXT NOT NULL,
Genero TEXT NOT NULL, Genero TEXT NOT NULL,
@ -98,19 +99,19 @@ app.MapPost("/livros", () =>
}); });
// Edita um livro. // Edita um livro.
app.MapPut("/livros/{isbn}", () => app.MapPut("/livros/{id}", () =>
{ {
}); });
// Obtém os dados de um livro individual. // Obtém os dados de um livro individual.
app.MapGet("/livros/{isbn}", () => app.MapGet("/livros/{id}", () =>
{ {
}); });
// Remove um livro. // Remove um livro.
app.MapDelete("/livros/{isbn}", () => app.MapDelete("/livros/{id}", () =>
{ {
}); });
@ -126,6 +127,7 @@ app.Run();`}</code></pre>
public class Livro public class Livro
{ {
public long Id { get; set; }
public string? Isbn { get; set; } public string? Isbn { get; set; }
public string? Titulo { get; set; } public string? Titulo { get; set; }
public string? Autor { get; set; } public string? Autor { get; set; }
@ -184,7 +186,7 @@ public class LivroRepository
</p> </p>
<p> <p>
O valor de retorno será um <code>IEnumerable&lt;Livro&gt;</code>. Um <code>IEnumerable</code> é uma interface do dotnet que representa O valor de retorno será um <code>IEnumerable&lt;Livro&gt;</code>. Um <code>IEnumerable</code> é uma interface que representa
qualquer sequência <em>iterável</em> de objetos. Por exemplo, um objeto <code>List&lt;T&gt;</code> ou um vetor <code>T[]</code> são iteráveis, qualquer sequência <em>iterável</em> de objetos. Por exemplo, um objeto <code>List&lt;T&gt;</code> ou um vetor <code>T[]</code> são iteráveis,
portanto podem ser retornados neste método. portanto podem ser retornados neste método.
</p> </p>
@ -215,7 +217,7 @@ public class LivroRepository
var offset = take * Math.Max(pagina-1, 0); var offset = take * Math.Max(pagina-1, 0);
var lista = new List<Livro>(); var lista = new List<Livro>();
cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro ORDER BY CriadoEm LIMIT @offset,@take"; cmd.CommandText = "SELECT Id, Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro ORDER BY CriadoEm LIMIT @offset,@take";
cmd.Parameters.AddWithValue("offset", offset); cmd.Parameters.AddWithValue("offset", offset);
cmd.Parameters.AddWithValue("take", take); cmd.Parameters.AddWithValue("take", take);
@ -225,16 +227,17 @@ public class LivroRepository
{ {
lista.Add(new() lista.Add(new()
{ {
Isbn = reader.GetString(0), Id = reader.GetInt64(0),
Titulo = reader.GetString(1), Isbn = reader.GetString(1),
Autor = reader.GetString(2), Titulo = reader.GetString(2),
Genero = reader.GetString(3), Autor = reader.GetString(3),
Descricao = reader.GetString(4), Genero = reader.GetString(4),
Foto = reader.GetString(5), Descricao = reader.GetString(5),
Keywords = reader.GetString(6), Foto = reader.GetString(6),
Ativo = reader.GetBoolean(7), Keywords = reader.GetString(7),
CriadoEm = reader.GetDateTime(8), Ativo = reader.GetBoolean(8),
AtualizadoEm = reader.GetDateTime(9), CriadoEm = reader.GetDateTime(9),
AtualizadoEm = reader.GetDateTime(10),
}); });
} }
@ -268,7 +271,7 @@ await conn.OpenAsync();`}</code></pre>
<p> <p>
O <a href="https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/" target="_blank">modelo de programação assíncrona</a> é uma característica que O <a href="https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/" target="_blank">modelo de programação assíncrona</a> é uma característica que
é praticamente ubíqua no dotnet, mas não precisamos nos aprofundar nela por enquanto. Basta saber que devemos utilizar <code>await</code> em métodos assíncronos, e está presente em quase toda a linguagem, mas não precisamos nos aprofundar nela por enquanto. Basta saber que devemos utilizar <code>await</code> em métodos assíncronos, e
que se o utilizarmos, o nosso método deve ser também assíncrono, sendo declarado com <code>async Task</code>. que se o utilizarmos, o nosso método deve ser também assíncrono, sendo declarado com <code>async Task</code>.
</p> </p>
@ -292,7 +295,7 @@ var lista = new List<Livro>();`}</code></pre>
A variável <code>lista</code> é uma lista de objetos <code>Livro</code> que irá guardar todos os resultados da consulta, e depois será retornada. A variável <code>lista</code> é uma lista de objetos <code>Livro</code> que irá guardar todos os resultados da consulta, e depois será retornada.
</p> </p>
<pre><code>{`cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro ORDER BY CriadoEm LIMIT @offset,@take"; <pre><code>{`cmd.CommandText = "SELECT Id, Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro ORDER BY CriadoEm LIMIT @offset,@take";
cmd.Parameters.AddWithValue("offset", offset); cmd.Parameters.AddWithValue("offset", offset);
cmd.Parameters.AddWithValue("take", take);`}</code></pre> cmd.Parameters.AddWithValue("take", take);`}</code></pre>
@ -312,24 +315,26 @@ cmd.Parameters.AddWithValue("take", take);`}</code></pre>
{ {
lista.Add(new() lista.Add(new()
{ {
Isbn = reader.GetString(0), Id = reader.GetInt64(0),
Titulo = reader.GetString(1), Isbn = reader.GetString(1),
Autor = reader.GetString(2), Titulo = reader.GetString(2),
Genero = reader.GetString(3), Autor = reader.GetString(3),
Descricao = reader.GetString(4), Genero = reader.GetString(4),
Foto = reader.GetString(5), Descricao = reader.GetString(5),
Keywords = reader.GetString(6), Foto = reader.GetString(6),
Ativo = reader.GetBoolean(7), Keywords = reader.GetString(7),
CriadoEm = reader.GetDateTime(8), Ativo = reader.GetBoolean(8),
AtualizadoEm = reader.GetDateTime(9), CriadoEm = reader.GetDateTime(9),
AtualizadoEm = reader.GetDateTime(10),
}); });
}`}</code></pre> }`}</code></pre>
<p> <p>
Este laço é repetido enquanto <code>await reader.ReadAsync()</code> retornar <code>true</code>, o que significa que existe um resultado da consulta que pode ser lido. Este laço é repetido enquanto <code>await reader.ReadAsync()</code> retornar <code>true</code>, o que significa que existe um resultado da consulta que pode ser lido.
Quando isso acontece, podemos ler as colunas do resultado através dos métodos de <code>reader</code>, como <code>reader.GetString(0)</code>. O número passado como Quando isso acontece, podemos ler as colunas do resultado através dos métodos de <code>reader</code>, como <code>reader.GetString(0)</code>. O número passado como
parâmetro para estas funções indica a posição da coluna lida. Por exemplo, na consulta que realizamos (<code>SELECT Isbn, Titulo, ...</code>), a coluna <code>Isbn</code> é a primeira, parâmetro para estas funções indica a posição da coluna lida. Por exemplo, na consulta que realizamos (<code>SELECT Id, Isbn, Titulo, ...</code>), a coluna <code>Id</code> é a primeira,
portanto, para ler este valor, devemos executar <code>reader.GetString(0)</code>. <code>Titulo</code> é a segunda, então o seu valor é obtido através de <code>reader.GetString(1)</code>, e assim portanto, para ler este valor, devemos executar <code>reader.GetInt64(0)</code> (<code>Int64</code>, um número inteiro de 64-bits, é um sinônimo para <code>long</code>).
<code>Isbn</code> é a segunda, então o seu valor é obtido através de <code>reader.GetString(1)</code>, e assim
sucessivamente. sucessivamente.
</p> </p>
@ -362,21 +367,21 @@ app.MapGet("/livros", async () =>
Agora execute este endpoint. Se tudo deu certo, você verá alguns livros cadastrados no retorno. Agora execute este endpoint. Se tudo deu certo, você verá alguns livros cadastrados no retorno.
</p> </p>
<h2>Obtendo um livro pelo ISBN</h2> <h2>Obtendo um livro pela chave primária</h2>
<p> <p>
O ISBN é a chave primária da tabela. Vamos implementar um método que consulta um único livro a partir deste dado. O Id é a chave primária da tabela. Vamos implementar um método que consulta um único livro a partir deste dado.
</p> </p>
<pre><code>{`public async Task<Livro> Obter(string isbn) <pre><code>{`public async Task<Livro> Obter(long id)
{ {
using var conn = new MySqlConnection(ConnString); using var conn = new MySqlConnection(ConnString);
using var cmd = conn.CreateCommand(); using var cmd = conn.CreateCommand();
await conn.OpenAsync(); await conn.OpenAsync();
cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro WHERE Isbn=@isbn"; cmd.CommandText = "SELECT Id, Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro WHERE Id=@id";
cmd.Parameters.AddWithValue("isbn", isbn); cmd.Parameters.AddWithValue("id", id);
using var reader = await cmd.ExecuteReaderAsync(); using var reader = await cmd.ExecuteReaderAsync();
@ -384,21 +389,22 @@ app.MapGet("/livros", async () =>
if (!existe) if (!existe)
{ {
throw new Exception($"Livro com ISBN {isbn} não encontrado"); throw new Exception($"Livro {id} não encontrado");
} }
return new() return new()
{ {
Isbn = reader.GetString(0), Id = reader.GetInt64(0),
Titulo = reader.GetString(1), Isbn = reader.GetString(1),
Autor = reader.GetString(2), Titulo = reader.GetString(2),
Genero = reader.GetString(3), Autor = reader.GetString(3),
Descricao = reader.GetString(4), Genero = reader.GetString(4),
Foto = reader.GetString(5), Descricao = reader.GetString(5),
Keywords = reader.GetString(6), Foto = reader.GetString(6),
Ativo = reader.GetBoolean(7), Keywords = reader.GetString(7),
CriadoEm = reader.GetDateTime(8), Ativo = reader.GetBoolean(8),
AtualizadoEm = reader.GetDateTime(9), CriadoEm = reader.GetDateTime(9),
AtualizadoEm = reader.GetDateTime(10),
}; };
}`}</code></pre> }`}</code></pre>
@ -406,11 +412,11 @@ app.MapGet("/livros", async () =>
As três primeiras linhas deste método, assim como no de listagem, abre uma conexão com o banco de dados. As três primeiras linhas deste método, assim como no de listagem, abre uma conexão com o banco de dados.
</p> </p>
<pre><code>{`cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro WHERE Isbn=@isbn"; <pre><code>{`cmd.CommandText = "SELECT Id, Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro WHERE Id=@id";
cmd.Parameters.AddWithValue("isbn", isbn);`}</code></pre> cmd.Parameters.AddWithValue("id", id);`}</code></pre>
<p> <p>
A consulta é montada na variável <code>cmd</code>, o parâmetro <code>isbn</code> é adicionado ao comando. A consulta é montada na variável <code>cmd</code>, o parâmetro <code>id</code> é adicionado ao comando.
</p> </p>
<pre><code>{`using var reader = await cmd.ExecuteReaderAsync(); <pre><code>{`using var reader = await cmd.ExecuteReaderAsync();
@ -419,27 +425,28 @@ var existe = await reader.ReadAsync();
if (!existe) if (!existe)
{ {
throw new Exception($"Livro com ISBN {isbn} não encontrado"); throw new Exception($"Livro {id} não encontrado");
} }
return new() return new()
{ {
Isbn = reader.GetString(0), Id = reader.GetInt64(0),
Titulo = reader.GetString(1), Isbn = reader.GetString(1),
Autor = reader.GetString(2), Titulo = reader.GetString(2),
Genero = reader.GetString(3), Autor = reader.GetString(3),
Descricao = reader.GetString(4), Genero = reader.GetString(4),
Foto = reader.GetString(5), Descricao = reader.GetString(5),
Keywords = reader.GetString(6), Foto = reader.GetString(6),
Ativo = reader.GetBoolean(7), Keywords = reader.GetString(7),
CriadoEm = reader.GetDateTime(8), Ativo = reader.GetBoolean(8),
AtualizadoEm = reader.GetDateTime(9), CriadoEm = reader.GetDateTime(9),
AtualizadoEm = reader.GetDateTime(10),
};`}</code></pre> };`}</code></pre>
<p> <p>
Assim como na listagem, <code>await cmd.ExecuteReaderAsync()</code> é chamado para ler os resultados da consulta. Assim como na listagem, <code>await cmd.ExecuteReaderAsync()</code> é chamado para ler os resultados da consulta.
Como esperamos que só haja um registro com aquele ISBN, executamos <code>await reader.ReadAsync()</code> apenas uma vez. Como esperamos que só haja um registro com aquele Id, executamos <code>await reader.ReadAsync()</code> apenas uma vez.
Se este método retorna <code>false</code>, é por que nenhum registro foi encontrado com aquele ISBN. Neste caso, lançamos um erro. Se este método retorna <code>false</code>, é por que nenhum registro foi encontrado com esta chave primária. Neste caso, lançamos um erro.
Do contrário, utilizamos os métodos de leitura do registro lido para montar um objeto <code>Livro</code> que será retornado. Do contrário, utilizamos os métodos de leitura do registro lido para montar um objeto <code>Livro</code> que será retornado.
</p> </p>
@ -448,10 +455,10 @@ return new()
</p> </p>
<pre><code>{`// Obtém os dados de um livro individual. <pre><code>{`// Obtém os dados de um livro individual.
app.MapGet("/livros/{isbn}", async (string isbn) => app.MapGet("/livros/{id}", async (long id) =>
{ {
var repo = new LivroRepository(); var repo = new LivroRepository();
var res = await repo.Obter(isbn); var res = await repo.Obter(id);
return res; return res;
});`}</code></pre> });`}</code></pre>
@ -492,11 +499,6 @@ app.MapGet("/livros/{isbn}", async (string isbn) =>
AtualizadoEm = default, AtualizadoEm = default,
}; };
if (livro.Isbn == "")
{
throw new Exception("O ISBN do livro é obrigatório.");
}
if (livro.Titulo == "") if (livro.Titulo == "")
{ {
throw new Exception("O título do livro é obrigatório."); throw new Exception("O título do livro é obrigatório.");
@ -570,12 +572,7 @@ app.MapGet("/livros/{isbn}", async (string isbn) =>
Fazemos isso nos campos do objeto <code>livro</code> para garantir que nenhum deles seja nulo, já que no banco de dados, todos os campos da tabela são <code>NOT NULL</code>. Fazemos isso nos campos do objeto <code>livro</code> para garantir que nenhum deles seja nulo, já que no banco de dados, todos os campos da tabela são <code>NOT NULL</code>.
</p> </p>
<pre><code>{`if (livro.Isbn == "") <pre><code>{`if (livro.Titulo == "")
{
throw new Exception("O ISBN do livro é obrigatório.");
}
if (livro.Titulo == "")
{ {
throw new Exception("O título do livro é obrigatório."); throw new Exception("O título do livro é obrigatório.");
}`}</code></pre> }`}</code></pre>
@ -635,14 +632,14 @@ app.MapPost("/livros", async (Livro livro) =>
O método de edição é similar ao de criação. O método de edição é similar ao de criação.
</p> </p>
<pre><code>{`public async Task<Livro> Editar(string isbn, Livro dados) <pre><code>{`public async Task<Livro> Editar(long id, Livro dados)
{ {
using var conn = new MySqlConnection(ConnString); using var conn = new MySqlConnection(ConnString);
using var cmd = conn.CreateCommand(); using var cmd = conn.CreateCommand();
await conn.OpenAsync(); await conn.OpenAsync();
var livro = await Obter(isbn); var livro = await Obter(id);
livro.Titulo = dados.Titulo?.Trim() ?? ""; livro.Titulo = dados.Titulo?.Trim() ?? "";
livro.Autor = dados.Autor?.Trim() ?? ""; livro.Autor = dados.Autor?.Trim() ?? "";
@ -656,10 +653,10 @@ app.MapPost("/livros", async (Livro livro) =>
@" @"
UPDATE Livro SET UPDATE Livro SET
Titulo=@titulo, Autor=@autor, Genero=@genero, Descricao=@descricao, Foto=@foto, Keywords=@keywords, AtualizadoEm=@atualizadoem Titulo=@titulo, Autor=@autor, Genero=@genero, Descricao=@descricao, Foto=@foto, Keywords=@keywords, AtualizadoEm=@atualizadoem
WHERE Isbn=@isbn WHERE Id=@id
"; ";
cmd.Parameters.AddWithValue("isbn", isbn); cmd.Parameters.AddWithValue("id", id);
cmd.Parameters.AddWithValue("titulo", livro.Titulo); cmd.Parameters.AddWithValue("titulo", livro.Titulo);
cmd.Parameters.AddWithValue("autor", livro.Autor); cmd.Parameters.AddWithValue("autor", livro.Autor);
cmd.Parameters.AddWithValue("genero", livro.Genero); cmd.Parameters.AddWithValue("genero", livro.Genero);
@ -677,7 +674,7 @@ app.MapPost("/livros", async (Livro livro) =>
O método recebe dois parâmetros, um isbn que identifica o livro que será editado, e um objeto com os novos dados do livro. O método recebe dois parâmetros, um isbn que identifica o livro que será editado, e um objeto com os novos dados do livro.
</p> </p>
<pre><code>{`var livro = await Obter(isbn); <pre><code>{`var livro = await Obter(id);
livro.Titulo = dados.Titulo?.Trim() ?? ""; livro.Titulo = dados.Titulo?.Trim() ?? "";
livro.Autor = dados.Autor?.Trim() ?? ""; livro.Autor = dados.Autor?.Trim() ?? "";
@ -688,7 +685,7 @@ livro.Keywords = dados.Keywords?.Trim() ?? "";
livro.AtualizadoEm = DateTime.Now;`}</code></pre> livro.AtualizadoEm = DateTime.Now;`}</code></pre>
<p> <p>
Chamamos a função <code>Obter(string)</code> que criamos para obter do banco de dados o livro que será editado. Em seguida, atribuímos a este objeto Chamamos a função <code>Obter(long)</code> que criamos para obter do banco de dados o livro que será editado. Em seguida, atribuímos a este objeto
os valores do objeto <code>dados</code>, formatando-os devidamente. os valores do objeto <code>dados</code>, formatando-os devidamente.
</p> </p>
@ -696,10 +693,10 @@ livro.AtualizadoEm = DateTime.Now;`}</code></pre>
@" @"
UPDATE Livro SET UPDATE Livro SET
Titulo=@titulo, Autor=@autor, Genero=@genero, Descricao=@descricao, Foto=@foto, Keywords=@keywords, AtualizadoEm=@atualizadoem Titulo=@titulo, Autor=@autor, Genero=@genero, Descricao=@descricao, Foto=@foto, Keywords=@keywords, AtualizadoEm=@atualizadoem
WHERE Isbn=@isbn WHERE Id=@id
"; ";
cmd.Parameters.AddWithValue("isbn", isbn); cmd.Parameters.AddWithValue("id", id);
cmd.Parameters.AddWithValue("titulo", livro.Titulo); cmd.Parameters.AddWithValue("titulo", livro.Titulo);
cmd.Parameters.AddWithValue("autor", livro.Autor); cmd.Parameters.AddWithValue("autor", livro.Autor);
cmd.Parameters.AddWithValue("genero", livro.Genero); cmd.Parameters.AddWithValue("genero", livro.Genero);
@ -721,10 +718,10 @@ return livro;`}</code></pre>
</p> </p>
<pre><code>{`// Edita um livro. <pre><code>{`// Edita um livro.
app.MapPut("/livros/{isbn}", async (string isbn, Livro livro) => app.MapPut("/livros/{id}", async (long id, Livro livro) =>
{ {
var repo = new LivroRepository(); var repo = new LivroRepository();
var res = await repo.Editar(isbn, livro); var res = await repo.Editar(id, livro);
return res; return res;
});`}</code></pre> });`}</code></pre>
@ -737,15 +734,15 @@ app.MapPut("/livros/{isbn}", async (string isbn, Livro livro) =>
Portanto, o método de remover será basicamente uma edição. Portanto, o método de remover será basicamente uma edição.
</p> </p>
<pre><code>{`public async Task Desativar(string isbn) <pre><code>{`public async Task Desativar(long id)
{ {
using var conn = new MySqlConnection(ConnString); using var conn = new MySqlConnection(ConnString);
using var cmd = conn.CreateCommand(); using var cmd = conn.CreateCommand();
await conn.OpenAsync(); await conn.OpenAsync();
cmd.CommandText = "UPDATE Livro SET Ativo=false WHERE Isbn=@isbn"; cmd.CommandText = "UPDATE Livro SET Ativo=false WHERE Id=@id";
cmd.Parameters.AddWithValue("isbn", isbn); cmd.Parameters.AddWithValue("id", id);
await cmd.ExecuteNonQueryAsync(); await cmd.ExecuteNonQueryAsync();
}`}</code></pre> }`}</code></pre>
@ -755,11 +752,11 @@ app.MapPut("/livros/{isbn}", async (string isbn, Livro livro) =>
</p> </p>
<pre><code>{`// Remove um livro. <pre><code>{`// Remove um livro.
app.MapDelete("/livros/{isbn}", async (string isbn) => app.MapDelete("/livros/{id}", async (long id) =>
{ {
var repo = new LivroRepository(); var repo = new LivroRepository();
await repo.Desativar(isbn); await repo.Desativar(id);
});`}</code></pre> });`}</code></pre>
<h2>Finalizando</h2> <h2>Finalizando</h2>

View File

@ -114,7 +114,7 @@ Donald Knuth - The Art of Computer Programming`}</code></pre>
</section> </section>
<p> <p>
E se desejássemos obter um único livro, identificado pelo seu ISBN. Poderíamos fazer um endpoint: E se desejássemos obter um único livro, identificado pela sua chave primária. Poderíamos fazer um endpoint:
</p> </p>
<pre><code>{`Request: <pre><code>{`Request:
@ -125,7 +125,7 @@ Response:
Yasunari Kawabata - Contos da Palma da Mão`}</code></pre> Yasunari Kawabata - Contos da Palma da Mão`}</code></pre>
<p> <p>
Note que agora a URI ficou <code>/livros/<b>9788574481371</b></code> onde <code><b>9788574481371</b></code> é o ISBN do livro, é um dado que o identifica e pode ser uma chave primária. Note que agora a URI ficou <code>/livros/<b>9788574481371</b></code> onde <code><b>9788574481371</b></code> é a chave primária do livro, o dado que o identifica.
</p> </p>
<p> <p>
@ -140,7 +140,7 @@ Response:
200 OK 200 OK
<livro> <livro>
<isbn>9788574481371</isbn> <id>9788574481371</id>
<titulo>Contos da Palma da Mão</titulo> <titulo>Contos da Palma da Mão</titulo>
<autor>Yasunari Kawabata</autor> <autor>Yasunari Kawabata</autor>
</livro>`}</code></pre> </livro>`}</code></pre>
@ -155,7 +155,7 @@ GET /livros/9788574481371
Response: Response:
200 Ok 200 Ok
{ {
"isbn": "9788574481371", "id": "9788574481371",
"titulo": "Contos da Palma da Mão", "titulo": "Contos da Palma da Mão",
"autor": "Yasunari Kawabata" "autor": "Yasunari Kawabata"
}`}</code></pre> }`}</code></pre>
@ -168,7 +168,6 @@ Response:
POST /livros POST /livros
{ {
"isbn": "9780262510875",
"titulo": "Structure and Interpretation of Computer Programs", "titulo": "Structure and Interpretation of Computer Programs",
"autor": "Gerald Jay Sussman" "autor": "Gerald Jay Sussman"
} }
@ -176,7 +175,7 @@ POST /livros
Response: Response:
201 Created 201 Created
{ {
"isbn": "9780262510875", "id": "9780262510875",
"titulo": "Structure and Interpretation of Computer Programs", "titulo": "Structure and Interpretation of Computer Programs",
"autor": "Gerald Jay Sussman" "autor": "Gerald Jay Sussman"
}`}</code></pre> }`}</code></pre>
@ -194,7 +193,7 @@ Response:
PUT /livros/9780262510875 PUT /livros/9780262510875
{ {
"isbn": "9780262510875", "id": "9780262510875",
"titulo": "Structure and Interpretation of Computer Programs, 2nd ed.", "titulo": "Structure and Interpretation of Computer Programs, 2nd ed.",
"autor": "Harold Abelson, Gerald Jay Sussman, Julie Sussman" "autor": "Harold Abelson, Gerald Jay Sussman, Julie Sussman"
} }
@ -202,7 +201,7 @@ PUT /livros/9780262510875
Response: Response:
200 Ok 200 Ok
{ {
"isbn": "9780262510875", "id": "9780262510875",
"titulo": "Structure and Interpretation of Computer Programs, 2nd ed.", "titulo": "Structure and Interpretation of Computer Programs, 2nd ed.",
"autor": "Harold Abelson, Gerald Jay Sussman, Julie Sussman" "autor": "Harold Abelson, Gerald Jay Sussman, Julie Sussman"
}`}</code></pre> }`}</code></pre>
@ -243,10 +242,10 @@ Response:
<ul> <ul>
<li><code>GET /livros</code> para obter todos os livros no sistema;</li> <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>GET /livros/{`{id}`}</code> para obter um único livro, identificado pela sua chave primária;</li>
<li><code>POST /livros</code> para inserir um novo livro;</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>PUT /livros/{`{id}`}</code> para editar um livro;</li>
<li><code>DELETE /livros/{`{isbn}`}</code> para remover um livro.</li> <li><code>DELETE /livros/{`{id}`}</code> para remover um livro.</li>
</ul> </ul>
<p> <p>
@ -285,13 +284,14 @@ app.Run();
public class Livro public class Livro
{ {
public long Id { get; set; }
public string? Isbn { get; set; } public string? Isbn { get; set; }
public string? Titulo { get; set; } public string? Titulo { get; set; }
public string? Autor { get; set; } public string? Autor { get; set; }
};`}</code></pre> };`}</code></pre>
<p> <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. Adicione esta classe no fim do arquivo. Todos os campos, com exceção do <code>Id</code>, 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. 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>
@ -311,18 +311,21 @@ app.MapGet("/livros", () =>
{ {
new() new()
{ {
Id = 1,
Isbn = "9780262510875", Isbn = "9780262510875",
Titulo = "Structure and Interpretation of Computer Programs", Titulo = "Structure and Interpretation of Computer Programs",
Autor = "Gerald Jay Sussman" Autor = "Gerald Jay Sussman"
}, },
new() new()
{ {
Id = 2,
Isbn = "9780131103627", Isbn = "9780131103627",
Titulo = "C Programming Language: ANSI C Version", Titulo = "C Programming Language: ANSI C Version",
Autor = "Dennis Ritchie, Brian Kerningham" Autor = "Dennis Ritchie, Brian Kerningham"
}, },
new() new()
{ {
Id = 3,
Isbn = "9780134190440", Isbn = "9780134190440",
Titulo = "The Go Programming Language", Titulo = "The Go Programming Language",
Autor = "Brian Kerningham" Autor = "Brian Kerningham"
@ -450,14 +453,14 @@ Content-Type: application/json
</p> </p>
<pre><code>{`// Edita um livro. <pre><code>{`// Edita um livro.
app.MapPut("/livros/{isbn}", (string isbn, Livro livro) => app.MapPut("/livros/{id}", (long id, Livro livro) =>
{ {
return new { editando = isbn, dados = livro }; return new { editando = id, dados = livro };
});`}</code></pre> });`}</code></pre>
<p> <p>
A url que definimos agora possui um <em>parâmetro</em> definido por <code>"{`{isbn}`}"</code>. Isto significa que este pedaço da A url que definimos agora possui um <em>parâmetro</em> definido por <code>"{`{id}`}"</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. url delimitado por chaves pode assumir qualquer valor numérico, por exemplo, <code>PUT /livros/100</code>, <code>PUT /livros/200</code>, etc.
</p> </p>
<p> <p>
@ -466,7 +469,7 @@ app.MapPut("/livros/{isbn}", (string isbn, Livro livro) =>
<pre><code>{`### Edita um livro <pre><code>{`### Edita um livro
PUT {{url}}/livros/9780321741769 PUT {{url}}/livros/1
Accept: application/json Accept: application/json
Content-Type: application/json Content-Type: application/json
@ -481,17 +484,17 @@ Content-Type: application/json
</div> </div>
<p> <p>
Agora, só falta o <code>GET /livros/{`{isbn}`}</code> e o <code>DELETE /livros/{`{isbn}`}</code>: Agora, só falta o <code>GET /livros/{`{id}`}</code> e o <code>DELETE /livros/{`{id}`}</code>:
</p> </p>
<pre><code>{`// Obtém os dados de um livro individual. <pre><code>{`// Obtém os dados de um livro individual.
app.MapGet("/livros/{isbn}", (string isbn) => app.MapGet("/livros/{id}", (long id) =>
{ {
return new Livro() { Isbn = isbn }; return new Livro() { Id = id };
}); });
// Remove um livro. // Remove um livro.
app.MapDelete("/livros/{isbn}", (string isbn) => app.MapDelete("/livros/{id}", (long id) =>
{ {
return Results.NoContent(); return Results.NoContent();
});`}</code></pre> });`}</code></pre>
@ -502,12 +505,12 @@ app.MapDelete("/livros/{isbn}", (string isbn) =>
<pre><code>{`### Obtém um livro individual <pre><code>{`### Obtém um livro individual
GET {{url}}/livros/9780321741769 GET {{url}}/livros/2
Accept: application/json Accept: application/json
### Remove um livro ### Remove um livro
DELETE {{url}}/livros/9780321741769 DELETE {{url}}/livros/3
Accept: application/json`}</code></pre> Accept: application/json`}</code></pre>
<p> <p>
@ -519,5 +522,10 @@ Accept: application/json`}</code></pre>
<p> <p>
O código fonte do projeto até agora pode ser baixado <a href="https://git.gbrl.dev/Gabriel/Biblioteca/archive/etapa2.zip">aqui</a>. 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> </p>
<nav>
<span>Próximo:</span>
<a href="/bd">Conectando com o banco de dados</a>
</nav>
</section> </section>
</section> </section>