Crie uma API RESTful com .NET Core e MongoDB
Avalie esse Tutorial
{Se você tem acompanhado meu conteúdo de desenvolvimento, deve se lembrar que recentemente escrevi Construa seu primeiro aplicativo .NET Core com o MongoDB Atlas, que se concentrou na criação de um aplicativo de console integrado ao MongoDB. Embora o MongoDB seja adequado para aplicativos de console, muitos desenvolvedores o acharão mais valioso em aplicativos web.
Neste tutorial, vamos expandir o anterior e criar uma API RESTful com endpoints que executam operações básicas de criação, leitura, atualização e exclusão (CRUD) no MongoDB Atlas.
Para que este tutorial cumpra sua finalidade, você precisa resolver alguns detalhes primeiro:
- Um cluster do MongoDB Atlas implantado e configurado, M0 ou superior
- .NET Core 6+
Não passaremos pelas etapas de implantação de um MongoDB Atlas cluster ou de configuração com regras de usuário e rede. Se você precisar de ajuda com isso, confira um tutorial anterior que foi publicado sobre o tópico.
Usaremos o .NET Core 6.0 neste tutorial, mas outras versões ainda podem funcionar. Apenas leve a versão em consideração antes de continuar.
Para começar, vamos criar um novo projeto do .NET Core usando o modelo de aplicativo web que a Microsoft oferece. Para fazer isso, execute os seguintes comandos da CLI:
1 dotnet new webapi -o MongoExample 2 cd MongoExample 3 dotnet add package MongoDB.Driver
Os comandos acima criam um novo projeto de aplicativo da web para o .NET Core e instalam o driver do MongoDB mais recente. Ficaremos com alguns arquivos boilerplate como parte do modelo, mas podemos removê-los.
Dentro do projeto, exclua todos os arquivos relacionados a
WeatherForecast
e similares.Antes de começarmos a projetar cada um dos endpoints da API RESTful com o .NET Core, precisamos criar e configurar nosso serviço MongoDB e definir o modelo de dados para nossa API.
Começaremos trabalhando em nosso serviço MongoDB, que será responsável por estabelecer nossa conexão e trabalhar diretamente com documentos dentro do MongoDB. Dentro do projeto, crie "Models/MongoDBSettings.cs" e adicione o seguinte código C#:
1 namespace MongoExample.Models; 2 3 public class MongoDBSettings { 4 5 public string ConnectionURI { get; set; } = null!; 6 public string DatabaseName { get; set; } = null!; 7 public string CollectionName { get; set; } = null!; 8 9 }
A classe
MongoDBSettings
acima contém informações sobre nossa conexão, o nome do banco de dados e o nome da coleção. Os dados que planejamos armazenar nesses campos de classe são encontrados no arquivo "appsettings.json" do projeto. Abra-o e adicione o seguinte:1 { 2 "Logging": { 3 "LogLevel": { 4 "Default": "Information", 5 "Microsoft.AspNetCore": "Warning" 6 } 7 }, 8 "AllowedHosts": "*", 9 "MongoDB": { 10 "ConnectionURI": "ATLAS_URI_HERE", 11 "DatabaseName": "sample_mflix", 12 "CollectionName": "playlist" 13 } 14 }
Anote especificamente o campo
MongoDB
. Assim como no projeto de exemplo anterior, usaremos o banco de dados "sample_mflix" e a coleção "playlist". Você precisará pegar a stringConnectionURI
do seu dashboard do MongoDB Atlas.Com as configurações definidas, podemos passar para a criação do serviço.
Crie "Services/MongoDBService.cs" dentro do seu projeto e adicione o seguinte:
1 using MongoExample.Models; 2 using Microsoft.Extensions.Options; 3 using MongoDB.Driver; 4 using MongoDB.Bson; 5 6 namespace MongoExample.Services; 7 8 public class MongoDBService { 9 10 private readonly IMongoCollection<Playlist> _playlistCollection; 11 12 public MongoDBService(IOptions<MongoDBSettings> mongoDBSettings) { 13 MongoClient client = new MongoClient(mongoDBSettings.Value.ConnectionURI); 14 IMongoDatabase database = client.GetDatabase(mongoDBSettings.Value.DatabaseName); 15 _playlistCollection = database.GetCollection<Playlist>(mongoDBSettings.Value.CollectionName); 16 } 17 18 public async Task<List<Playlist>> GetAsync() { } 19 public async Task CreateAsync(Playlist playlist) { } 20 public async Task AddToPlaylistAsync(string id, string movieId) {} 21 public async Task DeleteAsync(string id) { } 22 23 }
No código acima, cada uma das funções assíncronas foi deixada em branco de propósito. Preencheremos essas funções à medida que criarmos nossos pontos de extremidade. Em vez disso, anote o método do construtor e como estamos pegando as configurações passadas que vimos em nosso arquivo "appsettings.json" e definindo-as como variáveis. No final, a única variável com a qual interagiremos para este exemplo é a variável
_playlistCollection
.Com o serviço disponível, precisamos conectá-lo ao aplicativo. Abra o "Program.cs" do arquivo do projeto e adicione o seguinte no topo:
1 using MongoExample.Models; 2 using MongoExample.Services; 3 4 var builder = WebApplication.CreateBuilder(args); 5 6 builder.Services.Configure<MongoDBSettings>(builder.Configuration.GetSection("MongoDB")); 7 builder.Services.AddSingleton<MongoDBService>();
É provável que você já tenha a variável
builder
em seu código porque ela fazia parte do projeto padrão, portanto, não a adicione duas vezes. O que você precisará adicionar próximo ao topo é uma importação para seus modelos e serviços personalizados, bem como a configuração do serviço.Lembra do campo
MongoDB
no arquivo "appsettings.json"? Essa é a seção da qual a função GetSection
está extraindo. Essas informações são passadas para o serviço singleton que criamos.Com o serviço criado e funcionando, com exceção das funções assíncronas incompletas, podemos nos concentrar na criação de um modelo de dados para a nossa coleção.
Crie "Models/playlist.cs" e adicione o seguinte código C#:
1 using MongoDB.Bson; 2 using MongoDB.Bson.Serialization.Attributes; 3 using System.Text.Json.Serialization; 4 5 namespace MongoExample.Models; 6 7 public class Playlist { 8 9 [ ]10 [ ]11 public string? Id { get; set; } 12 13 public string username { get; set; } = null!; 14 15 [ ]16 [ ]17 public List<string> movieIds { get; set; } = null!; 18 19 }
Há algumas coisas acontecendo na classe acima que a transformam de uma classe C# padrão em algo que pode se integrar perfeitamente a um documento MongoDB.
Primeiro, você pode observar o seguinte:
1 [ ]2 [ ]3 public string? Id { get; set; }
Estamos dizendo que o campo
Id
deve ser representado como um ObjectId no BSON e o campo _id
no MongoDB. No entanto, quando trabalharmos com ele localmente em nosso aplicativo, ele será uma string.A próxima coisa que você notará é o seguinte:
1 [ ]2 [ ]3 public List<string> movieIds { get; set; } = null!;
Embora planejemos trabalhar com
movieIds
em nosso aplicativo C#, no MongoDB, o campo será conhecido como items
e, ao enviar ou receber JSON, o campo também será conhecido como items
em vez de movieIds
.Não é necessário definir mapeamentos personalizados se você planeja que o campo de classe local corresponda diretamente ao campo do documento. Veja o campo
username
em nosso exemplo. Ele não tem mapeamentos personalizados, portanto seráusername
em C#, username
em JSON e username
em MongoDB.Assim, temos um serviço do MongoDB e um document model para nossa coleção para trabalhar com o .NET Core.
Ao criar pontos de extremidade CRUD para este projeto, precisaremos alternar entre dois locais diferentes dentro do nosso projeto. Precisaremos definir o ponto de extremidade em um controlador e fazer o trabalho em nosso serviço.
Crie "Controllers/PlaylistController.cs" e adicione o seguinte código:
1 using System; 2 using Microsoft.AspNetCore.Mvc; 3 using MongoExample.Services; 4 using MongoExample.Models; 5 6 namespace MongoExample.Controllers; 7 8 [ ]9 [ ]10 public class PlaylistController: Controller { 11 12 private readonly MongoDBService _mongoDBService; 13 14 public PlaylistController(MongoDBService mongoDBService) { 15 _mongoDBService = mongoDBService; 16 } 17 18 [ ]19 public async Task<List<Playlist>> Get() {} 20 21 [ ]22 public async Task<IActionResult> Post([FromBody] Playlist playlist) {} 23 24 [ ]25 public async Task<IActionResult> AddToPlaylist(string id, [FromBody] string movieId) {} 26 27 [ ]28 public async Task<IActionResult> Delete(string id) {} 29 30 }
Na classe
PlaylistController
acima, temos um método construtor que obtém acesso à nossa classe de serviço singleton. Então temos uma série de endpoints para este controlador específico. Poderíamos adicionar muito mais endpoints ao nosso controlador, mas isso não é necessário para este exemplo.Vamos começar criando dados por meio do ponto de extremidade POST. Para fazer isso, é melhor começar no arquivo "Services/MongoDBService.cs" :
1 public async Task CreateAsync(Playlist playlist) { 2 await _playlistCollection.InsertOneAsync(playlist); 3 return; 4 }
Definimos o
_playlistCollection
no método construtor do serviço, portanto, agora podemos usar o método InsertOneAsync
, pegando uma variável Playlist
passada e inserindo-a. Voltando ao site "Controllers/PlaylistController.cs,", podemos adicionar o seguinte:1 [ ]2 public async Task<IActionResult> Post([FromBody] Playlist playlist) { 3 await _mongoDBService.CreateAsync(playlist); 4 return CreatedAtAction(nameof(Get), new { id = playlist.Id }, playlist); 5 }
O que estamos dizendo é que, quando o ponto de extremidade é executado, pegamos o objeto
Playlist
da solicitação, algo que o .NET Core analisa para nós, e o passamos para a função CreateAsync
que vimos no serviço. Após a inserção, retornamos algumas informações sobre a interação.É importante observar que, neste projeto de exemplo, não validaremos nenhum dado que flua de solicitações HTTP.
Vamos passar às operações de leitura.
Volte para o arquivo "Services/MongoDBService.cs" e adicione a seguinte função:
1 public async Task<List<Playlist>> GetAsync() { 2 return await _playlistCollection.Find(new BsonDocument()).ToListAsync(); 3 }
A operação
Find
acima retornará todos os documento existentes na coleção. Se quiser, você pode usar FindOne
ou fornecer critérios de filtro para retornar somente os dados desejados. Exploraremos os filtros em breve.Com a função de serviço pronta, adicione o seguinte endpoint ao "Controllers/PlaylistController.cs" arquivo:
1 [ ]2 public async Task<List<Playlist>> Get() { 3 return await _mongoDBService.GetAsync(); 4 }
Nada mal, né? Faremos o mais ou menos o mesmo com os outros endpoints.
O próximo estágio do CRUD é a atualização dos dados. No arquivo "Services/MongoDBService.cs" , adicione a seguinte função:
1 public async Task AddToPlaylistAsync(string id, string movieId) { 2 FilterDefinition<Playlist> filter = Builders<Playlist>.Filter.Eq("Id", id); 3 UpdateDefinition<Playlist> update = Builders<Playlist>.Update.AddToSet<string>("movieIds", movieId); 4 await _playlistCollection.UpdateOneAsync(filter, update); 5 return; 6 }
Em vez de fazer alterações em todo o documento, estamos planejando adicionar um item à nossa lista de reprodução e nada mais. Para fazer isso, configuramos um filtro de correspondência para determinar qual documento ou documentos devem receber a atualização. Nesse caso, estamos fazendo a correspondência com o ID, que será exclusivo. Em seguida, definimos o critério de atualização, que é uma operação
AddToSet
que só adicionará um item ao array se ele ainda não estiver no array.O método
UpdateOneAsync
atualiza somente um documento, mesmo que o filtro de correspondência retorne mais de uma correspondência.No arquivo "Controllers/playlistController.cs", adicione o seguinte ponto de extremidade para emparelhar com a função
AddToPlayListAsync
:1 [ ]2 public async Task<IActionResult> AddToPlaylist(string id, [FromBody] string movieId) { 3 await _mongoDBService.AddToPlaylistAsync(id, movieId); 4 return NoContent(); 5 }
No endpoint PUT acima, estamos pegando o
id
dos parâmetros da rota e o movieId
do corpo da solicitação e usando-os com a função AddToPlaylistAsync
.Isso nos leva à nossa parte final do espectro CRUD. Nós vamos cuidar da exclusão de dados.
No arquivo "Services/MongoDBService.cs" , adicione a seguinte função:
1 public async Task DeleteAsync(string id) { 2 FilterDefinition<Playlist> filter = Builders<Playlist>.Filter.Eq("Id", id); 3 await _playlistCollection.DeleteOneAsync(filter); 4 return; 5 }
A função acima excluirá um único documento com base nos critérios do filtro. Os critérios do filtro, neste caso, são uma correspondência no ID que sempre será exclusivo. Seus filtros podem ser mais extravagantes, se você quiser.
Para finalizar, o endpoint dessa função teria a seguinte aparência no arquivo "Controllers/PlaylistController.cs"
1 [ ]2 public async Task<IActionResult> Delete(string id) { 3 await _mongoDBService.DeleteAsync(id); 4 return NoContent(); 5 }
Criamos apenas quatro pontos de extremidade, mas você poderia pegar tudo o que fizemos e criar mais 100 se quisesse. Todos eles usariam uma estratégia semelhante e podem aproveitar tudo o que o MongoDB tem a oferecer.
Agora que você implementou o código, pode usar os quatro pontos de extremidade da API RESTful.
Ao executar o aplicativo, você deverá ver uma UI do Swagger, conforme abaixo:
Você pode testar isso clicando no botão "Experimentar" em cada endpoint. É mais fácil começar com o endpoint POST
/api/Playlist/
para adicionar alguns dados ao banco de dados. Os dados a seguir podem ser usados como dados fictícios:1 { 2 "username": "nraboy", 3 "items": [ 4 "1234", 5 "5678" 6 ] 7 }
A execução desse procedimento insere esses dados e permite buscá-los no GET e usar o valor de ID gerado para testar os outros pontos de extremidade.
Você acabou de ver como criar uma API RESTful simples de quatro endpoints usando o .NET Core e o MongoDB. Essa foi uma expansão do tutorial anterior, que abordou o mesmo uso do MongoDB, mas em um formato de aplicativo de console em vez de aplicativo web.
Como mencionei, você pode aplicar essa mesma estratégia em mais pontos de extremidade, cada um fazendo algo crítico para seu aplicativo da web.