Introdução
Um breve curso sobre como criar um aplicativo Web simples usando tecnologias ASP.NET Core, Entity Framework, Microsoft SQL Server DBMS e Angular framework. Vamos testar a API Web por meio do aplicativo Postman .
O curso consiste em várias partes:
- Crie APIs da Web com ASP.NET Web API e Entity Framework Core.
- Implementação da IU em Angular.
- Adicionando autenticação ao aplicativo.
- Estenda o modelo de aplicativo e explore recursos adicionais do Entity Framework.
Parte 1. Construir APIs da Web com ASP.NET Web API e Entity Framework Core
Como exemplo, vamos considerar a aplicação clássica de uma lista de tarefas pendentes. Usarei o Visual Studio 2019 para desenvolver o aplicativo (o processo é semelhante no Visual Studio 2017).
Criação de projeto
Crie um novo projeto de aplicativo da Web ASP.NET Core no Visual Studio:
Nomeie o aplicativo e especifique o caminho para o diretório com o projeto:
E selecione o modelo de aplicativo API:
Modelo
Vamos criar um catálogo de modelos e adicionar a primeira classe TodoItem.cs ao novo catálogo, cujos objetos irão descrever algumas tarefas da lista de tarefas no aplicativo:
public class TodoItem
{
public int Id { get; set; }
public string TaskDescription { get; set; }
public bool IsComplete { get; set; }
}
Usaremos Sql Server como um DBMS e o banco de dados será acessado por meio do Entity Framework Core e, primeiro, instalaremos a estrutura por meio do gerenciador de pacotes NuGet integrado:
Uma abordagem para trabalhar com o Entity Framework é a abordagem "Code-First". A essência da abordagem é que com base no modelo de aplicação (no nosso caso, o modelo representa uma única classe - TodoItem.cs), a estrutura do banco de dados (tabelas, chaves primárias, links) é formada, todo esse trabalho acontece “nos bastidores” e diretamente com Não trabalhamos com SQL. Um pré-requisito para a classe de modelo é a presença de um campo de chave primária. Por padrão, o Entity Framework procura um campo inteiro em cujo nome existe uma substring "id" e forma uma chave primária com base nele. Você pode substituir esse comportamento usando atributos especiais ou usando os recursos da API Fluent.
O principal componente para trabalhar com o Entity Framework é a classe de contexto do banco de dados, por meio da qual os dados nas tabelas são realmente acessados:
public class EFTodoDBContext : DbContext
{
public EFTodoDBContext(DbContextOptions<EFTodoDBContext> options) : base(options)
{ }
public DbSet<TodoItem> TodoItems{ get; set; }
}
A classe base DbContext cria o contexto do banco de dados e fornece acesso à funcionalidade do Entity Framework.
Usaremos o SQL Server 2017 Express para armazenar dados de aplicativos . Strings de conexão são armazenados em um arquivo JSON chamado appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=Todo;Trusted_Connection=true"
}
}
Em seguida, você precisa modificar a classe Startup.cs adicionando o seguinte código ao método ConfigureServices ():
services.AddDbContext<EFTodoDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
O método AddDbContext () configura os serviços fornecidos pelo Entity Framework Core para a classe de contexto do banco de dados EFTodoDBContext. O argumento para o método AddDbContext () é uma expressão lambda que recebe um objeto de opções que configura o banco de dados para a classe de contexto. Nesse caso, o banco de dados é configurado usando o método UseSqlServer () e especificando uma string de conexão.
Vamos definir as operações básicas para trabalhar com tarefas na interface ITodoRepository:
public interface ITodoRepository
{
IEnumerable<TodoItem> Get();
TodoItem Get(int id);
void Create(TodoItem item);
void Update(TodoItem item);
TodoItem Delete(int id);
}
Esta interface permite que não pensemos na implementação específica do data warehouse, talvez não tenhamos decidido exatamente sobre a escolha de um framework SGBD ou ORM, agora não importa, a classe que descreve o acesso aos dados herdará desta interface.
Vamos implementar um repositório que, como mencionado anteriormente, herdará de ITodoRepository e usará EFTodoDBContext como fonte de dados:
public class EFTodoRepository : ITodoRepository
{
private EFTodoDBContext Context;
public IEnumerable<TodoItem> Get()
{
return Context.TodoItems;
}
public TodoItem Get(int Id)
{
return Context.TodoItems.Find(Id);
}
public EFTodoRepository(EFTodoDBContext context)
{
Context = context;
}
public void Create(TodoItem item)
{
Context.TodoItems.Add(item);
Context.SaveChanges();
}
public void Update(TodoItem updatedTodoItem)
{
TodoItem currentItem = Get(updatedTodoItem.Id);
currentItem.IsComplete = updatedTodoItem.IsComplete;
currentItem.TaskDescription = updatedTodoItem.TaskDescription;
Context.TodoItems.Update(currentItem);
Context.SaveChanges();
}
public TodoItem Delete(int Id)
{
TodoItem todoItem = Get(Id);
if (todoItem != null)
{
Context.TodoItems.Remove(todoItem);
Context.SaveChanges();
}
return todoItem;
}
}
Controlador
O controlador, cuja implementação será descrita a seguir, não saberá nada sobre o contexto dos dados do EFTodoDBContext, mas apenas utilizará a interface ITodoRepository em seu trabalho, o que permite alterar a fonte dos dados sem alterar o controlador. Esta abordagem Adam Freeman em seu livro "Entity Framework Core 2 para ASP.NET Core MVC para profissionais" chamou o padrão "Storage".
O controlador implementa manipuladores para métodos de solicitação HTTP padrão: GET, POST, PUT, DELETE, que irão alterar o estado de nossas tarefas descritas na classe TodoItem.cs.
Adicione a classe TodoController.cs ao diretório Controllers com o seguinte conteúdo:
[Route("api/[controller]")]
public class TodoController : Controller
{
ITodoRepository TodoRepository;
public TodoController(ITodoRepository todoRepository)
{
TodoRepository = todoRepository;
}
[HttpGet(Name = "GetAllItems")]
public IEnumerable<TodoItem> Get()
{
return TodoRepository.Get();
}
[HttpGet("{id}", Name = "GetTodoItem")]
public IActionResult Get(int Id)
{
TodoItem todoItem = TodoRepository.Get(Id);
if (todoItem == null)
{
return NotFound();
}
return new ObjectResult(todoItem);
}
[HttpPost]
public IActionResult Create([FromBody] TodoItem todoItem)
{
if (todoItem == null)
{
return BadRequest();
}
TodoRepository.Create(todoItem);
return CreatedAtRoute("GetTodoItem", new { id = todoItem.Id }, todoItem);
}
[HttpPut("{id}")]
public IActionResult Update(int Id, [FromBody] TodoItem updatedTodoItem)
{
if (updatedTodoItem == null || updatedTodoItem.Id != Id)
{
return BadRequest();
}
var todoItem = TodoRepository.Get(Id);
if (todoItem == null)
{
return NotFound();
}
TodoRepository.Update(updatedTodoItem);
return RedirectToRoute("GetAllItems");
}
[HttpDelete("{id}")]
public IActionResult Delete(int Id)
{
var deletedTodoItem = TodoRepository.Delete(Id);
if (deletedTodoItem == null)
{
return BadRequest();
}
return new ObjectResult(deletedTodoItem);
}
}
Um atributo que descreve o modelo de rota para acessar o controlador é especificado antes da definição da classe: [Route ("api / [controlador]")]. O TodoController estará acessível pela seguinte rota: https: // <host ip>: <port> / api / todo. [Controller] especifica o nome da classe do controlador em letras minúsculas, omitindo a parte "Controlador".
Antes de cada método ser definido no TodoController, um atributo especial do formulário é especificado: [<método HTTP> ("parâmetro", Nome = "alias do método")]. O atributo determina qual solicitação HTTP será processada por este método, o parâmetro que é passado no URI da solicitação e o alias do método com o qual a solicitação pode ser reenviada. Se você não especificar o atributo, por padrão, o framework MVC tentará encontrar o método mais adequado no controlador para processar a solicitação com base no nome do método e nos parâmetros especificados na solicitação, portanto, se você não especificar um atributo para o método Get () no controlador TodoController, então em uma solicitação HTTP usando o método GET: https: // <host ip>: <port> / api / todo, a infraestrutura definirá o método Get () do controlador para processar a solicitação.
Em seu construtor, o controlador recebe uma referência a um objeto do tipo ITodoRepository, mas até o momento a infraestrutura MVC não sabe qual objeto substituir na criação do controlador. Precisamos criar um serviço que resolva exclusivamente essa dependência, para isso faremos algumas alterações na classe Startup.cs adicionando o seguinte código ao método ConfigureServices ():
services.AddTransient<ITodoRepository, EFTodoRepository>();
O método AddTransient <ITodoRepository, EFTodoRepository> () define um serviço que cria uma nova instância da classe EFTodoRepository sempre que uma instância do tipo ITodoRepository é necessária, por exemplo em um controlador.
O código completo da classe Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddDbContext<EFTodoDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddTransient<ITodoRepository, EFTodoRepository>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Migrações
Para que o Entity Framework gere o banco de dados e as tabelas do modelo, você deve usar o processo de migração do banco de dados. As migrações são um grupo de comandos que prepara o banco de dados para funcionar com o Entity Framework. Eles são usados para criar e sincronizar o banco de dados. Os comandos podem ser executados no Console do gerenciador de pacotes e no Power Shell (Developer Power Shell). Usaremos o Console do gerenciador de pacotes, para trabalhar com o Entity Framework precisamos instalar o pacote Microsoft.EntityFrameworkCore.Tools:
Inicie o console do gerenciador de pacotes e execute o comando Add-Migration Initial :
Um novo diretório aparecerá no projeto - Migrations, no qual as classes de migração serão armazenadas, com base nos quais os objetos no banco de dados serão criados após a execução do comando Update-Database:
A API Web está pronta, executando o aplicativo no IIS Express local, podemos testar o controlador.
Testando WebAPI
Vamos criar uma nova coleção de solicitações no Postman chamada TodoWebAPI:
Como nosso banco de dados está vazio, vamos testar a criação de uma nova tarefa primeiro. No controlador, o método Create () é responsável pela criação de tarefas, que irão processar uma requisição HTTP enviada pelo método POST e conter um objeto TodoItem serializado em formato JSON no corpo da requisição. O atributo [FromBody] antes do parâmetro todoItem no método Create () diz à estrutura MVC para desserializar o objeto TodoItem do corpo da solicitação e passá-lo como um parâmetro para o método. Vamos criar uma solicitação no Postman que enviará uma solicitação ao webAPI para criar uma nova tarefa:
O método Create () após a criação bem-sucedida da tarefa redireciona a solicitação para o método Get () com o alias "GetTodoItem" e passa o Id da tarefa recém-criada como um parâmetro, como resultado, receberemos o objeto de tarefa criado no formato JSON em resposta à solicitação.
Enviando uma solicitação HTTP usando o método PUT e especificando um objeto já criado no URI Id (https: // localhost: 44370 / api / todo / 1), e passando um objeto com algumas alterações no formato JSON no corpo da solicitação, vamos alterar este objeto no banco de dados :
Com uma solicitação HTTP com o método GET sem especificar parâmetros, receberemos todos os objetos no banco de dados:
Uma solicitação HTTP com o método DELETE e especificando o ID do objeto no URI (https: // localhost: 44370 / api / todo / 2), excluirá o objeto do banco de dados e retornará JSON com tarefa remota:
Isso é tudo, na próxima parte implementaremos a interface do usuário usando a estrutura Angular JavaScript.