Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Note
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 10 de este artículo.
Warning
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 10 de este artículo.
Por Pratik Khandelwal y Scott Addie
En este tutorial se crea una API web que ejecuta operaciones de creación, lectura, actualización y eliminación (CRUD) en una base de datos NoSQL de MongoDB.
En este tutorial aprenderá a:
- Configuración de MongoDB
- Crear una base de datos de MongoDB
- Definir un esquema y una colección de MongoDB
- Realizar operaciones de CRUD de MongoDB desde una API web
- Personalizar la serialización de JSON
Prerequisites
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Configuración de MongoDB
Habilite el acceso a MongoDB y al shell de MongoDB desde cualquier lugar en el equipo de desarrollo (Windows/Linux/macOS):
Descargue e instale el shell de MongoDB:
- macOS/Linux: elija un directorio para extraer el shell de MongoDB. Agregue la ruta de acceso resultante para
mongosha la variable de entornoPATH. - Windows: El shell de MongoDB (mongosh.exe) se instala en C:\Users\<user>\AppData\Local\Programs\mongosh. Agregue la ruta de acceso resultante para
mongosh.exea la variable de entornoPATH.
- macOS/Linux: elija un directorio para extraer el shell de MongoDB. Agregue la ruta de acceso resultante para
Descargue e instale MongoDB:
- macOS/Linux: compruebe el directorio en el que se instaló MongoDB, normalmente en /usr/local/mongodb. Agregue la ruta de acceso resultante para
mongodba la variable de entornoPATH. - Windows: MongoDB está instalado en C:\Archivos de programa\MongoDB de forma predeterminada. Agregue C:\Archivos de programa\MongoDB\Server<número_versión>\bin a la variable de entorno
PATH.
- macOS/Linux: compruebe el directorio en el que se instaló MongoDB, normalmente en /usr/local/mongodb. Agregue la ruta de acceso resultante para
Elija un directorio de almacenamiento de datos: seleccione un directorio en el equipo de desarrollo para almacenar los datos. Si no existe el directorio, créelo. El shell de MongoDB no crea nuevos directorios:
- macOS/Linux: por ejemplo,
/usr/local/var/mongodb. - Windows: por ejemplo,
C:\\BooksData.
- macOS/Linux: por ejemplo,
En el shell de comandos del sistema operativo (no en el shell de MongoDB), use el siguiente comando para conectarse a MongoDB en el puerto predeterminado 27017. Reemplace
<data_directory_path>por el directorio elegido en el paso anterior.mongod --dbpath <data_directory_path>
Use el shell de MongoDB instalado previamente en los pasos siguientes para crear una base de datos, hacer colecciones y almacenar documentos. Para más información sobre los comandos de shell de MongoDB, vea mongosh.
Abra una instancia del shell de comandos de MongoDB iniciando
mongosh.exeo ejecutando el siguiente comando en el shell de comandos:mongoshEn el shell de comandos, conéctese a la base de datos de prueba predeterminada mediante la ejecución de:
use BookStoreSe crea una base de datos denominada BookStore si aún no existe. Si la base de datos existe, su conexión se abre para las transacciones.
Cree una colección
Bookscon el comando siguiente:db.createCollection('Books')Se muestra el siguiente resultado:
{ "ok" : 1 }Defina un esquema para la colección
Bookse inserte dos documentos con el comando siguiente:db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])Se muestra un resultado similar al siguiente:
{ "acknowledged" : true, "insertedIds" : [ ObjectId("61a6058e6c43f32854e51f51"), ObjectId("61a6058e6c43f32854e51f52") ] }Note
Los objetos
ObjectIdque se muestran en el resultado anterior no coincidirán con los que se muestran en el shell de comandos.Vea los documentos en la base de datos mediante el comando siguiente:
db.Books.find().pretty()Se muestra un resultado similar al siguiente:
{ "_id" : ObjectId("61a6058e6c43f32854e51f51"), "Name" : "Design Patterns", "Price" : 54.93, "Category" : "Computers", "Author" : "Ralph Johnson" } { "_id" : ObjectId("61a6058e6c43f32854e51f52"), "Name" : "Clean Code", "Price" : 43.15, "Category" : "Computers", "Author" : "Robert C. Martin" }El esquema agrega una propiedad
_idgenerada automáticamente del tipoObjectIdpara cada documento.
Creación de un proyecto de API web de ASP.NET Core
- Vaya a Archivo>Nuevo>Proyecto.
- Seleccione el tipo de proyecto API web de ASP.NET Core y, luego, Siguiente.
- Denomine el proyecto BookStoreApi y seleccione Siguiente.
- En el cuadro de diálogo Información adicional:
- Confirme que el Marco es .NET 9.0 (Soporte en un plazo estándar).
- Confirme que la casilla Usar controladores está activada.
- Confirme que la casilla Habilitar compatibilidad con OpenAPI está activada.
- Selecciona Crear.
En la ventana Consola del Administrador de paquetes, desplácese hasta la raíz del proyecto. Ejecute el siguiente comando para instalar el controlador .NET para MongoDB:
Install-Package MongoDB.Driver
Adición de un modelo de entidad
Agregue un directorio Modelos a la raíz del proyecto.
Agregue una clase
Bookal directorio Models con el código siguiente:using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace BookStoreApi.Models; public class Book { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } [BsonElement("Name")] public string BookName { get; set; } = null!; public decimal Price { get; set; } public string Category { get; set; } = null!; public string Author { get; set; } = null!; }En la clase anterior, se requiere la propiedad
Id:- para asignar el objeto de Common Language Runtime (CLR) a la colección de MongoDB.
- para anotarla con
[BsonId]a fin de designarla como clave principal del documento. - Anotado con
[BsonRepresentation(BsonType.ObjectId)]para permitir el paso del parámetro como tipostringen lugar de una estructura ObjectId. Mongo controla la conversión destringaObjectId.
La propiedad
BookNamese anota con el atributo[BsonElement]. El valorNamedel atributo representa el nombre de propiedad en la colección de MongoDB.
Agregar un modelo de configuración
Agregue los siguientes valores de configuración de base de datos a
appsettings.json:{ "BookStoreDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "BooksCollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }Agregue una clase
BookStoreDatabaseSettingsal directorio Models con el código siguiente:namespace BookStoreApi.Models; public class BookStoreDatabaseSettings { public string ConnectionString { get; set; } = null!; public string DatabaseName { get; set; } = null!; public string BooksCollectionName { get; set; } = null!; }La clase anterior
BookStoreDatabaseSettingsse utiliza para almacenar los valores de propiedadappsettings.jsondel archivoBookStoreDatabase. Los nombres de las propiedades de JSON y C# son nombrados idénticamente para facilitar la correspondencia.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase"));En el código anterior, la instancia de configuración a la que la sección
appsettings.jsondel archivoBookStoreDatabaseenlaza está registrada en el contenedor de inserción de dependencias (DI). Por ejemplo, una propiedadBookStoreDatabaseSettingsdel objetoConnectionStringse rellena con la propiedadBookStoreDatabase:ConnectionStringenappsettings.json.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBookStoreDatabaseSettings:using BookStoreApi.Models;
Adición de un servicio de operaciones CRUD
Agregue un directorio Servicios a la raíz del proyecto.
Agregue una clase
BooksServiceal directorio Servicios con el código siguiente:using BookStoreApi.Models; using Microsoft.Extensions.Options; using MongoDB.Driver; namespace BookStoreApi.Services; public class BooksService { private readonly IMongoCollection<Book> _booksCollection; public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); } public async Task<List<Book>> GetAsync() => await _booksCollection.Find(_ => true).ToListAsync(); public async Task<Book?> GetAsync(string id) => await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync(); public async Task CreateAsync(Book newBook) => await _booksCollection.InsertOneAsync(newBook); public async Task UpdateAsync(string id, Book updatedBook) => await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook); public async Task RemoveAsync(string id) => await _booksCollection.DeleteOneAsync(x => x.Id == id); }En el código anterior, se recuperó una instancia de
BookStoreDatabaseSettingsde la inserción de dependencias mediante la inserción de un constructor. Esta técnica proporciona acceso a los valores de configuración deappsettings.jsonque se agregaron en la sección Agregar un modelo de configuración.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>();En el código anterior, la clase
BooksServicese registra con inserción de dependencias para admitir la inserción del constructor en las clases de consumo. El tiempo de vida del servicio tipo singleton es el más apropiado porqueBooksServicetiene una dependencia directa deMongoClient. Según las instrucciones oficiales de reutilización de cliente Mongo,MongoClientdebe registrarse en la inserción de dependencias con una duración de servicio de tipo singleton.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBooksService:using BookStoreApi.Services;
La clase BooksService usa los miembros MongoDB.Driver siguientes para ejecutar operaciones CRUD en la base de datos:
MongoClient: lee la instancia del servidor para ejecutar operaciones de base de datos. El constructor de esta clase se proporciona en la cadena de conexión de MongoDB.
public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); }IMongoDatabase: representa la base de datos de Mongo para ejecutar operaciones. Este tutorial usa el método genérico GetCollection<TDocument>(collection) en la interfaz para tener acceso a los datos de una colección específica. Ejecute las operaciones CRUD en la colección después de llamar a este método. En la llamada al método
GetCollection<TDocument>(collection):-
collectionrepresenta el nombre de la colección. -
TDocumentrepresenta el tipo de objeto CLR almacenado en la colección.
-
GetCollection<TDocument>(collection) devuelve un objeto MongoCollection que representa la colección. En este tutorial, se invocan los métodos siguientes en la colección:
- DeleteOneAsync: elimina un único documento que cumpla los criterios de búsqueda proporcionados.
- Find<TDocument>: devuelve todos los documentos de la colección que cumplen los criterios de búsqueda indicados.
- InsertOneAsync: inserta el objeto proporcionado como un nuevo documento en la colección.
- ReplaceOneAsync: reemplaza un único documento que cumpla los criterios de búsqueda indicados por el objeto proporcionado.
Incorporación de un controlador
Agregue una clase BooksController al directorio Controllers con el código siguiente:
using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace BookStoreApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
private readonly BooksService _booksService;
public BooksController(BooksService booksService) =>
_booksService = booksService;
[HttpGet]
public async Task<List<Book>> Get() =>
await _booksService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<Book>> Get(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
return book;
}
[HttpPost]
public async Task<IActionResult> Post(Book newBook)
{
await _booksService.CreateAsync(newBook);
return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
}
[HttpPut("{id:length(24)}")]
public async Task<IActionResult> Update(string id, Book updatedBook)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
updatedBook.Id = book.Id;
await _booksService.UpdateAsync(id, updatedBook);
return NoContent();
}
[HttpDelete("{id:length(24)}")]
public async Task<IActionResult> Delete(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
await _booksService.RemoveAsync(id);
return NoContent();
}
}
El controlador de API web anterior:
- Usa la clase
BooksServicepara ejecutar operaciones CRUD. - Contiene métodos de acción para admitir las solicitudes GET, POST, PUT y DELETE de HTTP.
- Llama a CreatedAtAction en el método de acción
Createpara devolver una respuesta HTTP 201. El código de estado 201 es la respuesta estándar para un método HTTP POST que crea un recurso en el servidor.CreatedAtActiontambién agrega un encabezadoLocationa la respuesta. El encabezadoLocationespecifica el identificador URI del libro recién creado.
Configuración de las opciones de serialización de JSON
Hay dos detalles que cambiar sobre las respuestas JSON devueltas en la sección Prueba de la API web:
- Las mayúsculas y minúsculas Camel predeterminadas de los nombres de propiedad se deben cambiar para que coincidan con el uso de mayúsculas y minúsculas de Pascal de los nombres de propiedad del objeto CLR.
- La propiedad
bookNamese debe devolver comoName.
Para satisfacer los requisitos anteriores, realice los cambios siguientes:
En
Program.cs, encadena el siguiente código resaltado a la llamada al métodoAddControllers:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>(); builder.Services.AddControllers() .AddJsonOptions( options => options.JsonSerializerOptions.PropertyNamingPolicy = null);Con el cambio anterior, los nombres de propiedad de la respuesta JSON serializada de la API web coinciden con sus nombres de propiedad correspondientes en el tipo de objeto CLR. Por ejemplo, la propiedad
Bookde la claseAuthorse serializa comoAuthor, en lugar deauthor.En
Models/Book.cs, anote la propiedadBookNamecon el atributo[JsonPropertyName]:[BsonElement("Name")] [JsonPropertyName("Name")] public string BookName { get; set; } = null!;El valor
[JsonPropertyName]del atributoNamerepresenta el nombre de propiedad en la respuesta JSON serializada de la API web.Agregue el código siguiente en la parte superior de
Models/Book.cspara resolver la referencia al atributo[JsonProperty]:using System.Text.Json.Serialization;Repita los pasos definidos en la sección Prueba de la API web. Observe la diferencia en los nombres de propiedad JSON.
Prueba de la API web
En este tutorial se usan el Explorador de puntos de conexión y los archivos .http para probar la API.
Compile y ejecute la aplicación.
En Endpoints Explorer, haga clic con el botón derecho en el primer GET punto de conexión
/api/booksy seleccione Generar solicitud.El siguiente contenido se agrega al
BookStoreApi.httparchivo. Si es la primera vez que se genera una solicitud, el archivo se crea en la raíz del proyecto.@BookStoreApi_HostAddress = https://localhost:<port> GET {{BookStoreApi_HostAddress}}/api/books ###El número de puerto ya debe establecerse en el puerto usado por la aplicación, por ejemplo,
https://localhost:56874. Si no es así, puede encontrar el número de puerto en la ventana de salida al iniciar la aplicación.Seleccione el vínculo Enviar solicitud encima de la nueva
GETlínea de solicitud.La solicitud GET se envía a la aplicación y la respuesta se muestra en el panel Respuesta.
El cuerpo de la respuesta muestra el resultado JSON que contiene las entradas del libro similares a las siguientes:
[ { "Id": "61a6058e6c43f32854e51f51", "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Id": "61a6058e6c43f32854e51f52", "Name": "Clean Code", "Price": 43.15, "Category": "Computers", "Author": "Robert C. Martin" } ]Para recuperar un único libro, haga clic con el botón derecho en el
/api/books/{id}, params (string id)punto de conexión GET en el Explorador de puntos de conexión y seleccione Generar solicitud.El siguiente contenido se anexa al
BookStoreApi.httparchivo:@id=string GET {{BookStoreApi_HostAddress}}/api/books/{{id}} ###Reemplace
idla variable por uno de los identificadores devueltos de la solicitud anterior, por ejemplo:@id="61a6058e6c43f32854e51f52" GET {{BookStoreApi_HostAddress}}/api/books/{{id}} ###Seleccione el vínculo Enviar solicitud encima de la nueva
GETlínea de solicitud.La solicitud GET se envía a la aplicación y la respuesta se muestra en el panel Respuesta.
El cuerpo de la respuesta muestra un JSON similar al siguiente:
{ "Id": "61a6058e6c43f32854e51f52", "Name": "Clean Code", "Price": 43.15, "Category": "Computers", "Author": "Robert C. Martin" }Para probar el punto de conexión POST, haga clic con el botón derecho en el
/api/bookspunto de conexión POST y seleccione Generar solicitud.El siguiente contenido se agrega al archivo
BookStoreApi.http:POST {{BookStoreApi_HostAddress}}/api/books Content-Type: application/json { //Book } ###Reemplace el comentario Book por un objeto book como el cuerpo de la solicitud JSON:
POST {{BookStoreApi_HostAddress}}/api/books Content-Type: application/json { "Name": "The Pragmatic Programmer", "Price": 49.99, "Category": "Computers", "Author": "Andy Hunt" } ###Seleccione el vínculo Enviar solicitud encima de la línea de
POSTsolicitud.La solicitud POST se envía a la aplicación y la respuesta se muestra en el panel Respuesta . La respuesta debe incluir el libro recién creado con su identificador asignado.
Por último, para eliminar un libro, haga clic con el botón derecho en el
/api/books/{id}, params (string id)punto de conexión DELETE y seleccione Generar solicitud.El siguiente contenido se anexa al
BookStoreApi.httparchivo:DELETE {{BookStoreApi_HostAddress}}/api/Books/{{id}} ###Reemplace la
idvariable por uno de los identificadores devueltos de la solicitud anterior y haga clic en Enviar solicitud. Por ejemplo:DELETE {{BookStoreApi_HostAddress}}/api/Books/67f417517ce1b36aeab71236 ###
Agregar compatibilidad con la autenticación a una API web
ASP.NET Core Identity agrega la funcionalidad de inicio de sesión de la interfaz de usuario (IU) a las aplicaciones web de ASP.NET Core. Para proteger las API web y las SPA, usa una de las siguientes opciones:
Duende Identity Server es un marco de OpenID Connect y OAuth 2.0 para ASP.NET Core. Duende Identity Server permite las siguientes características de seguridad:
- Autenticación como servicio (AaaS)
- Inicio de sesión único (SSO) mediante varios tipos de aplicaciones
- Control de acceso para API
- Puerta de enlace de federación
Important
Duende Software puede requerir que pagues una tarifa de licencia para el uso en producción de Duende Identity Server. Para obtener más información, consulte Migración de ASP.NET Core en .NET 5 a .NET 6.
Para más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Recursos adicionales
En este tutorial se crea una API web que ejecuta operaciones de creación, lectura, actualización y eliminación (CRUD) en una base de datos NoSQL de MongoDB.
En este tutorial aprenderá a:
- Configuración de MongoDB
- Crear una base de datos de MongoDB
- Definir un esquema y una colección de MongoDB
- Realizar operaciones de CRUD de MongoDB desde una API web
- Personalizar la serialización de JSON
Prerequisites
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Configuración de MongoDB
Habilite el acceso a MongoDB y al shell de MongoDB desde cualquier lugar en el equipo de desarrollo (Windows/Linux/macOS):
Descargue e instale el shell de MongoDB:
- macOS/Linux: elija un directorio para extraer el shell de MongoDB. Agregue la ruta de acceso resultante para
mongosha la variable de entornoPATH. - Windows: El shell de MongoDB (mongosh.exe) se instala en C:\Users\<user>\AppData\Local\Programs\mongosh. Agregue la ruta de acceso resultante para
mongosh.exea la variable de entornoPATH.
- macOS/Linux: elija un directorio para extraer el shell de MongoDB. Agregue la ruta de acceso resultante para
Descargue e instale MongoDB:
- macOS/Linux: compruebe el directorio en el que se instaló MongoDB, normalmente en /usr/local/mongodb. Agregue la ruta de acceso resultante para
mongodba la variable de entornoPATH. - Windows: MongoDB está instalado en C:\Archivos de programa\MongoDB de forma predeterminada. Agregue C:\Archivos de programa\MongoDB\Server<número_versión>\bin a la variable de entorno
PATH.
- macOS/Linux: compruebe el directorio en el que se instaló MongoDB, normalmente en /usr/local/mongodb. Agregue la ruta de acceso resultante para
Elija un directorio de almacenamiento de datos: seleccione un directorio en el equipo de desarrollo para almacenar los datos. Si no existe el directorio, créelo. El shell de MongoDB no crea nuevos directorios:
- macOS/Linux: por ejemplo,
/usr/local/var/mongodb. - Windows: por ejemplo,
C:\\BooksData.
- macOS/Linux: por ejemplo,
En el shell de comandos del sistema operativo (no en el shell de MongoDB), use el siguiente comando para conectarse a MongoDB en el puerto predeterminado 27017. Reemplace
<data_directory_path>por el directorio elegido en el paso anterior.mongod --dbpath <data_directory_path>
Use el shell de MongoDB instalado previamente en los pasos siguientes para crear una base de datos, hacer colecciones y almacenar documentos. Para más información sobre los comandos de shell de MongoDB, vea mongosh.
Abra una instancia del shell de comandos de MongoDB iniciando
mongosh.exeo ejecutando el siguiente comando en el shell de comandos:mongoshEn el shell de comandos, conéctese a la base de datos de prueba predeterminada mediante la ejecución de:
use BookStoreSe crea una base de datos denominada BookStore si aún no existe. Si la base de datos existe, su conexión se abre para las transacciones.
Cree una colección
Bookscon el comando siguiente:db.createCollection('Books')Se muestra el siguiente resultado:
{ "ok" : 1 }Defina un esquema para la colección
Bookse inserte dos documentos con el comando siguiente:db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])Se muestra un resultado similar al siguiente:
{ "acknowledged" : true, "insertedIds" : [ ObjectId("61a6058e6c43f32854e51f51"), ObjectId("61a6058e6c43f32854e51f52") ] }Note
Los objetos
ObjectIdque se muestran en el resultado anterior no coincidirán con los que se muestran en el shell de comandos.Vea los documentos en la base de datos mediante el comando siguiente:
db.Books.find().pretty()Se muestra un resultado similar al siguiente:
{ "_id" : ObjectId("61a6058e6c43f32854e51f51"), "Name" : "Design Patterns", "Price" : 54.93, "Category" : "Computers", "Author" : "Ralph Johnson" } { "_id" : ObjectId("61a6058e6c43f32854e51f52"), "Name" : "Clean Code", "Price" : 43.15, "Category" : "Computers", "Author" : "Robert C. Martin" }El esquema agrega una propiedad
_idgenerada automáticamente del tipoObjectIdpara cada documento.
Creación de un proyecto de API web de ASP.NET Core
Vaya a Archivo>Nuevo>Proyecto.
Seleccione el tipo de proyecto API web de ASP.NET Core y, luego, Siguiente.
Denomine el proyecto BookStoreApi y seleccione Siguiente.
Seleccione el entorno .NET 8.0 (compatibilidad a largo plazo) y selecciona Crear.
En la ventana Consola del Administrador de paquetes, desplácese hasta la raíz del proyecto. Ejecute el siguiente comando para instalar el controlador .NET para MongoDB:
Install-Package MongoDB.Driver
Adición de un modelo de entidad
Agregue un directorio Modelos a la raíz del proyecto.
Agregue una clase
Bookal directorio Models con el código siguiente:using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace BookStoreApi.Models; public class Book { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } [BsonElement("Name")] public string BookName { get; set; } = null!; public decimal Price { get; set; } public string Category { get; set; } = null!; public string Author { get; set; } = null!; }En la clase anterior, se requiere la propiedad
Id:- para asignar el objeto de Common Language Runtime (CLR) a la colección de MongoDB.
- para anotarla con
[BsonId]a fin de designarla como clave principal del documento. - Anotado con
[BsonRepresentation(BsonType.ObjectId)]para permitir el paso del parámetro como tipostringen lugar de una estructura ObjectId. Mongo controla la conversión destringaObjectId.
La propiedad
BookNamese anota con el atributo[BsonElement]. El valorNamedel atributo representa el nombre de propiedad en la colección de MongoDB.
Agregar un modelo de configuración
Agregue los siguientes valores de configuración de base de datos a
appsettings.json:{ "BookStoreDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "BooksCollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }Agregue una clase
BookStoreDatabaseSettingsal directorio Models con el código siguiente:namespace BookStoreApi.Models; public class BookStoreDatabaseSettings { public string ConnectionString { get; set; } = null!; public string DatabaseName { get; set; } = null!; public string BooksCollectionName { get; set; } = null!; }La clase anterior
BookStoreDatabaseSettingsse utiliza para almacenar los valores de propiedadappsettings.jsondel archivoBookStoreDatabase. Los nombres de las propiedades de JSON y C# son nombrados idénticamente para facilitar la correspondencia.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase"));En el código anterior, la instancia de configuración a la que la sección
appsettings.jsondel archivoBookStoreDatabaseenlaza está registrada en el contenedor de inserción de dependencias (DI). Por ejemplo, una propiedadBookStoreDatabaseSettingsdel objetoConnectionStringse rellena con la propiedadBookStoreDatabase:ConnectionStringenappsettings.json.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBookStoreDatabaseSettings:using BookStoreApi.Models;
Adición de un servicio de operaciones CRUD
Agregue un directorio Servicios a la raíz del proyecto.
Agregue una clase
BooksServiceal directorio Servicios con el código siguiente:using BookStoreApi.Models; using Microsoft.Extensions.Options; using MongoDB.Driver; namespace BookStoreApi.Services; public class BooksService { private readonly IMongoCollection<Book> _booksCollection; public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); } public async Task<List<Book>> GetAsync() => await _booksCollection.Find(_ => true).ToListAsync(); public async Task<Book?> GetAsync(string id) => await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync(); public async Task CreateAsync(Book newBook) => await _booksCollection.InsertOneAsync(newBook); public async Task UpdateAsync(string id, Book updatedBook) => await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook); public async Task RemoveAsync(string id) => await _booksCollection.DeleteOneAsync(x => x.Id == id); }En el código anterior, se recuperó una instancia de
BookStoreDatabaseSettingsde la inserción de dependencias mediante la inserción de un constructor. Esta técnica proporciona acceso a los valores de configuración deappsettings.jsonque se agregaron en la sección Agregar un modelo de configuración.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>();En el código anterior, la clase
BooksServicese registra con inserción de dependencias para admitir la inserción del constructor en las clases de consumo. El tiempo de vida del servicio tipo singleton es el más apropiado porqueBooksServicetiene una dependencia directa deMongoClient. Según las instrucciones oficiales de reutilización de cliente Mongo,MongoClientdebe registrarse en la inserción de dependencias con una duración de servicio de tipo singleton.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBooksService:using BookStoreApi.Services;
La clase BooksService usa los miembros MongoDB.Driver siguientes para ejecutar operaciones CRUD en la base de datos:
MongoClient: lee la instancia del servidor para ejecutar operaciones de base de datos. El constructor de esta clase se proporciona en la cadena de conexión de MongoDB.
public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); }IMongoDatabase: representa la base de datos de Mongo para ejecutar operaciones. Este tutorial usa el método genérico GetCollection<TDocument>(collection) en la interfaz para tener acceso a los datos de una colección específica. Ejecute las operaciones CRUD en la colección después de llamar a este método. En la llamada al método
GetCollection<TDocument>(collection):-
collectionrepresenta el nombre de la colección. -
TDocumentrepresenta el tipo de objeto CLR almacenado en la colección.
-
GetCollection<TDocument>(collection) devuelve un objeto MongoCollection que representa la colección. En este tutorial, se invocan los métodos siguientes en la colección:
- DeleteOneAsync: elimina un único documento que cumpla los criterios de búsqueda proporcionados.
- Find<TDocument>: devuelve todos los documentos de la colección que cumplen los criterios de búsqueda indicados.
- InsertOneAsync: inserta el objeto proporcionado como un nuevo documento en la colección.
- ReplaceOneAsync: reemplaza un único documento que cumpla los criterios de búsqueda indicados por el objeto proporcionado.
Incorporación de un controlador
Agregue una clase BooksController al directorio Controllers con el código siguiente:
using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace BookStoreApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
private readonly BooksService _booksService;
public BooksController(BooksService booksService) =>
_booksService = booksService;
[HttpGet]
public async Task<List<Book>> Get() =>
await _booksService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<Book>> Get(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
return book;
}
[HttpPost]
public async Task<IActionResult> Post(Book newBook)
{
await _booksService.CreateAsync(newBook);
return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
}
[HttpPut("{id:length(24)}")]
public async Task<IActionResult> Update(string id, Book updatedBook)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
updatedBook.Id = book.Id;
await _booksService.UpdateAsync(id, updatedBook);
return NoContent();
}
[HttpDelete("{id:length(24)}")]
public async Task<IActionResult> Delete(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
await _booksService.RemoveAsync(id);
return NoContent();
}
}
El controlador de API web anterior:
- Usa la clase
BooksServicepara ejecutar operaciones CRUD. - Contiene métodos de acción para admitir las solicitudes GET, POST, PUT y DELETE de HTTP.
- Llama a CreatedAtAction en el método de acción
Createpara devolver una respuesta HTTP 201. El código de estado 201 es la respuesta estándar para un método HTTP POST que crea un recurso en el servidor.CreatedAtActiontambién agrega un encabezadoLocationa la respuesta. El encabezadoLocationespecifica el identificador URI del libro recién creado.
Prueba de la API web
Compile y ejecute la aplicación.
Vaya a
https://localhost:<port>/api/books, donde<port>es el número de puerto asignado automáticamente para la aplicación, para probar el método de acciónGetsin parámetros del controlador. Se muestra una respuesta JSON similar a la siguiente:[ { "id": "61a6058e6c43f32854e51f51", "bookName": "Design Patterns", "price": 54.93, "category": "Computers", "author": "Ralph Johnson" }, { "id": "61a6058e6c43f32854e51f52", "bookName": "Clean Code", "price": 43.15, "category": "Computers", "author": "Robert C. Martin" } ]Vaya a
https://localhost:<port>/api/books/{id here}para probar el método de acciónGetsobrecargado del controlador. Se muestra una respuesta JSON similar a la siguiente:{ "id": "61a6058e6c43f32854e51f52", "bookName": "Clean Code", "price": 43.15, "category": "Computers", "author": "Robert C. Martin" }
Configuración de las opciones de serialización de JSON
Hay dos detalles que cambiar sobre las respuestas JSON devueltas en la sección Prueba de la API web:
- Las mayúsculas y minúsculas Camel predeterminadas de los nombres de propiedad se deben cambiar para que coincidan con el uso de mayúsculas y minúsculas de Pascal de los nombres de propiedad del objeto CLR.
- La propiedad
bookNamese debe devolver comoName.
Para satisfacer los requisitos anteriores, realice los cambios siguientes:
En
Program.cs, encadena el siguiente código resaltado a la llamada al métodoAddControllers:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>(); builder.Services.AddControllers() .AddJsonOptions( options => options.JsonSerializerOptions.PropertyNamingPolicy = null);Con el cambio anterior, los nombres de propiedad de la respuesta JSON serializada de la API web coinciden con sus nombres de propiedad correspondientes en el tipo de objeto CLR. Por ejemplo, la propiedad
Bookde la claseAuthorse serializa comoAuthor, en lugar deauthor.En
Models/Book.cs, anote la propiedadBookNamecon el atributo[JsonPropertyName]:[BsonElement("Name")] [JsonPropertyName("Name")] public string BookName { get; set; } = null!;El valor
[JsonPropertyName]del atributoNamerepresenta el nombre de propiedad en la respuesta JSON serializada de la API web.Agregue el código siguiente en la parte superior de
Models/Book.cspara resolver la referencia al atributo[JsonProperty]:using System.Text.Json.Serialization;Repita los pasos definidos en la sección Prueba de la API web. Observe la diferencia en los nombres de propiedad JSON.
Agregar compatibilidad con la autenticación a una API web
ASP.NET Core Identity agrega la funcionalidad de inicio de sesión de la interfaz de usuario (IU) a las aplicaciones web de ASP.NET Core. Para proteger las API web y las SPA, usa una de las siguientes opciones:
Duende Identity Server es un marco de OpenID Connect y OAuth 2.0 para ASP.NET Core. Duende Identity Server permite las siguientes características de seguridad:
- Autenticación como servicio (AaaS)
- Inicio de sesión único (SSO) mediante varios tipos de aplicaciones
- Control de acceso para API
- Puerta de enlace de federación
Important
Duende Software puede requerir que pagues una tarifa de licencia para el uso en producción de Duende Identity Server. Para obtener más información, consulte Migración de ASP.NET Core en .NET 5 a .NET 6.
Para más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Recursos adicionales
En este tutorial se crea una API web que ejecuta operaciones de creación, lectura, actualización y eliminación (CRUD) en una base de datos NoSQL de MongoDB.
En este tutorial aprenderá a:
- Configuración de MongoDB
- Crear una base de datos de MongoDB
- Definir un esquema y una colección de MongoDB
- Realizar operaciones de CRUD de MongoDB desde una API web
- Personalizar la serialización de JSON
Prerequisites
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Configuración de MongoDB
Habilite el acceso a MongoDB y al shell de MongoDB desde cualquier lugar en el equipo de desarrollo:
En Windows, MongoDB está instalado en C:\Archivos de programa\MongoDB de forma predeterminada. Agregue C:\Archivos de programa\MongoDB\Server<número_versión>\bin a la variable de entorno
PATH.Descargue el shell de MongoDB y elija un directorio para extraerlo. Agregue la ruta de acceso resultante para
mongosh.exea la variable de entornoPATH.Elija un directorio en el equipo de desarrollo para almacenar los datos. Por ejemplo, C:\BooksData en Windows. Si no existe el directorio, créelo. El shell de mongo no crea nuevos directorios.
En el shell de comandos del sistema operativo (no en el shell de MongoDB), use el siguiente comando para conectarse a MongoDB en el puerto predeterminado 27017. Reemplace
<data_directory_path>por el directorio elegido en el paso anterior.mongod --dbpath <data_directory_path>
Use el shell de MongoDB instalado previamente en los pasos siguientes para crear una base de datos, hacer colecciones y almacenar documentos. Para más información sobre los comandos de shell de MongoDB, vea mongosh.
Abra una instancia del shell de comandos de MongoDB iniciando
mongosh.exeo ejecutando el siguiente comando en el shell de comandos:mongoshEn el shell de comandos, conéctese a la base de datos de prueba predeterminada mediante la ejecución de:
use BookStoreSe crea una base de datos denominada BookStore si aún no existe. Si la base de datos existe, su conexión se abre para las transacciones.
Cree una colección
Bookscon el comando siguiente:db.createCollection('Books')Se muestra el siguiente resultado:
{ "ok" : 1 }Defina un esquema para la colección
Bookse inserte dos documentos con el comando siguiente:db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])Se muestra un resultado similar al siguiente:
{ "acknowledged" : true, "insertedIds" : [ ObjectId("61a6058e6c43f32854e51f51"), ObjectId("61a6058e6c43f32854e51f52") ] }Note
Los objetos
ObjectIdque se muestran en el resultado anterior no coincidirán con los que se muestran en el shell de comandos.Vea los documentos en la base de datos mediante el comando siguiente:
db.Books.find().pretty()Se muestra un resultado similar al siguiente:
{ "_id" : ObjectId("61a6058e6c43f32854e51f51"), "Name" : "Design Patterns", "Price" : 54.93, "Category" : "Computers", "Author" : "Ralph Johnson" } { "_id" : ObjectId("61a6058e6c43f32854e51f52"), "Name" : "Clean Code", "Price" : 43.15, "Category" : "Computers", "Author" : "Robert C. Martin" }El esquema agrega una propiedad
_idgenerada automáticamente del tipoObjectIdpara cada documento.
Creación de un proyecto de API web de ASP.NET Core
Vaya a Archivo>Nuevo>Proyecto.
Seleccione el tipo de proyecto API web de ASP.NET Core y, luego, Siguiente.
Denomine el proyecto BookStoreApi y seleccione Siguiente.
Seleccione el marco .NET 7.0 (soporte a largo plazo) y, a continuación, seleccione Crear.
En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes.
En la ventana Consola del Administrador de paquetes, desplácese hasta la raíz del proyecto. Ejecute el siguiente comando para instalar el controlador .NET para MongoDB:
Install-Package MongoDB.Driver
Adición de un modelo de entidad
Agregue un directorio Modelos a la raíz del proyecto.
Agregue una clase
Bookal directorio Models con el código siguiente:using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace BookStoreApi.Models; public class Book { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } [BsonElement("Name")] public string BookName { get; set; } = null!; public decimal Price { get; set; } public string Category { get; set; } = null!; public string Author { get; set; } = null!; }En la clase anterior, se requiere la propiedad
Id:- para asignar el objeto de Common Language Runtime (CLR) a la colección de MongoDB.
- para anotarla con
[BsonId]a fin de designarla como clave principal del documento. - Anotado con
[BsonRepresentation(BsonType.ObjectId)]para permitir el paso del parámetro como tipostringen lugar de una estructura ObjectId. Mongo controla la conversión destringaObjectId.
La propiedad
BookNamese anota con el atributo[BsonElement]. El valorNamedel atributo representa el nombre de propiedad en la colección de MongoDB.
Agregar un modelo de configuración
Agregue los siguientes valores de configuración de base de datos a
appsettings.json:{ "BookStoreDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "BooksCollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }Agregue una clase
BookStoreDatabaseSettingsal directorio Models con el código siguiente:namespace BookStoreApi.Models; public class BookStoreDatabaseSettings { public string ConnectionString { get; set; } = null!; public string DatabaseName { get; set; } = null!; public string BooksCollectionName { get; set; } = null!; }La clase anterior
BookStoreDatabaseSettingsse utiliza para almacenar los valores de propiedadappsettings.jsondel archivoBookStoreDatabase. Los nombres de las propiedades de JSON y C# son nombrados idénticamente para facilitar la correspondencia.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase"));En el código anterior, la instancia de configuración a la que la sección
appsettings.jsondel archivoBookStoreDatabaseenlaza está registrada en el contenedor de inserción de dependencias (DI). Por ejemplo, una propiedadBookStoreDatabaseSettingsdel objetoConnectionStringse rellena con la propiedadBookStoreDatabase:ConnectionStringenappsettings.json.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBookStoreDatabaseSettings:using BookStoreApi.Models;
Adición de un servicio de operaciones CRUD
Agregue un directorio Servicios a la raíz del proyecto.
Agregue una clase
BooksServiceal directorio Servicios con el código siguiente:using BookStoreApi.Models; using Microsoft.Extensions.Options; using MongoDB.Driver; namespace BookStoreApi.Services; public class BooksService { private readonly IMongoCollection<Book> _booksCollection; public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); } public async Task<List<Book>> GetAsync() => await _booksCollection.Find(_ => true).ToListAsync(); public async Task<Book?> GetAsync(string id) => await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync(); public async Task CreateAsync(Book newBook) => await _booksCollection.InsertOneAsync(newBook); public async Task UpdateAsync(string id, Book updatedBook) => await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook); public async Task RemoveAsync(string id) => await _booksCollection.DeleteOneAsync(x => x.Id == id); }En el código anterior, se recuperó una instancia de
BookStoreDatabaseSettingsde la inserción de dependencias mediante la inserción de un constructor. Esta técnica proporciona acceso a los valores de configuración deappsettings.jsonque se agregaron en la sección Agregar un modelo de configuración.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>();En el código anterior, la clase
BooksServicese registra con inserción de dependencias para admitir la inserción del constructor en las clases de consumo. El tiempo de vida del servicio tipo singleton es el más apropiado porqueBooksServicetiene una dependencia directa deMongoClient. Según las instrucciones oficiales de reutilización de cliente Mongo,MongoClientdebe registrarse en la inserción de dependencias con una duración de servicio de tipo singleton.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBooksService:using BookStoreApi.Services;
La clase BooksService usa los miembros MongoDB.Driver siguientes para ejecutar operaciones CRUD en la base de datos:
MongoClient: lee la instancia del servidor para ejecutar operaciones de base de datos. Se proporciona la cadena de conexión de MongoDB al constructor de esta clase:
public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); }IMongoDatabase: representa la base de datos de Mongo para ejecutar operaciones. Este tutorial usa el método genérico GetCollection<TDocument>(collection) en la interfaz para tener acceso a los datos de una colección específica. Ejecute las operaciones CRUD en la colección después de llamar a este método. En la llamada al método
GetCollection<TDocument>(collection):-
collectionrepresenta el nombre de la colección. -
TDocumentrepresenta el tipo de objeto CLR almacenado en la colección.
-
GetCollection<TDocument>(collection) devuelve un objeto MongoCollection que representa la colección. En este tutorial, se invocan los métodos siguientes en la colección:
- DeleteOneAsync: elimina un único documento que cumpla los criterios de búsqueda proporcionados.
- Find<TDocument>: devuelve todos los documentos de la colección que cumplen los criterios de búsqueda indicados.
- InsertOneAsync: inserta el objeto proporcionado como un nuevo documento en la colección.
- ReplaceOneAsync: reemplaza un único documento que cumpla los criterios de búsqueda indicados por el objeto proporcionado.
Incorporación de un controlador
Agregue una clase BooksController al directorio Controllers con el código siguiente:
using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace BookStoreApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
private readonly BooksService _booksService;
public BooksController(BooksService booksService) =>
_booksService = booksService;
[HttpGet]
public async Task<List<Book>> Get() =>
await _booksService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<Book>> Get(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
return book;
}
[HttpPost]
public async Task<IActionResult> Post(Book newBook)
{
await _booksService.CreateAsync(newBook);
return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
}
[HttpPut("{id:length(24)}")]
public async Task<IActionResult> Update(string id, Book updatedBook)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
updatedBook.Id = book.Id;
await _booksService.UpdateAsync(id, updatedBook);
return NoContent();
}
[HttpDelete("{id:length(24)}")]
public async Task<IActionResult> Delete(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
await _booksService.RemoveAsync(id);
return NoContent();
}
}
El controlador de API web anterior:
- Usa la clase
BooksServicepara ejecutar operaciones CRUD. - Contiene métodos de acción para admitir las solicitudes GET, POST, PUT y DELETE de HTTP.
- Llama a CreatedAtAction en el método de acción
Createpara devolver una respuesta HTTP 201. El código de estado 201 es la respuesta estándar para un método HTTP POST que crea un recurso en el servidor.CreatedAtActiontambién agrega un encabezadoLocationa la respuesta. El encabezadoLocationespecifica el identificador URI del libro recién creado.
Prueba de la API web
Compile y ejecute la aplicación.
Vaya a
https://localhost:<port>/api/books, donde<port>es el número de puerto asignado automáticamente para la aplicación, para probar el método de acciónGetsin parámetros del controlador. Se muestra una respuesta JSON similar a la siguiente:[ { "id": "61a6058e6c43f32854e51f51", "bookName": "Design Patterns", "price": 54.93, "category": "Computers", "author": "Ralph Johnson" }, { "id": "61a6058e6c43f32854e51f52", "bookName": "Clean Code", "price": 43.15, "category": "Computers", "author": "Robert C. Martin" } ]Vaya a
https://localhost:<port>/api/books/{id here}para probar el método de acciónGetsobrecargado del controlador. Se muestra una respuesta JSON similar a la siguiente:{ "id": "61a6058e6c43f32854e51f52", "bookName": "Clean Code", "price": 43.15, "category": "Computers", "author": "Robert C. Martin" }
Configuración de las opciones de serialización de JSON
Hay dos detalles que cambiar sobre las respuestas JSON devueltas en la sección Prueba de la API web:
- Las mayúsculas y minúsculas Camel predeterminadas de los nombres de propiedad se deben cambiar para que coincidan con el uso de mayúsculas y minúsculas de Pascal de los nombres de propiedad del objeto CLR.
- La propiedad
bookNamese debe devolver comoName.
Para satisfacer los requisitos anteriores, realice los cambios siguientes:
En
Program.cs, encadena el siguiente código resaltado a la llamada al métodoAddControllers:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>(); builder.Services.AddControllers() .AddJsonOptions( options => options.JsonSerializerOptions.PropertyNamingPolicy = null);Con el cambio anterior, los nombres de propiedad de la respuesta JSON serializada de la API web coinciden con sus nombres de propiedad correspondientes en el tipo de objeto CLR. Por ejemplo, la propiedad
Bookde la claseAuthorse serializa comoAuthor, en lugar deauthor.En
Models/Book.cs, anote la propiedadBookNamecon el atributo[JsonPropertyName]:[BsonElement("Name")] [JsonPropertyName("Name")] public string BookName { get; set; } = null!;El valor
[JsonPropertyName]del atributoNamerepresenta el nombre de propiedad en la respuesta JSON serializada de la API web.Agregue el código siguiente en la parte superior de
Models/Book.cspara resolver la referencia al atributo[JsonProperty]:using System.Text.Json.Serialization;Repita los pasos definidos en la sección Prueba de la API web. Observe la diferencia en los nombres de propiedad JSON.
Agregar compatibilidad con la autenticación a una API web
ASP.NET Core Identity agrega la funcionalidad de inicio de sesión de la interfaz de usuario (IU) a las aplicaciones web de ASP.NET Core. Para proteger las API web y las SPA, usa una de las siguientes opciones:
Duende Identity Server es un marco de OpenID Connect y OAuth 2.0 para ASP.NET Core. Duende Identity Server permite las siguientes características de seguridad:
- Autenticación como servicio (AaaS)
- Inicio de sesión único (SSO) mediante varios tipos de aplicaciones
- Control de acceso para API
- Puerta de enlace de federación
Important
Duende Software puede requerir que pagues una tarifa de licencia para el uso en producción de Duende Identity Server. Para obtener más información, consulte Migración de ASP.NET Core en .NET 5 a .NET 6.
Para más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Recursos adicionales
En este tutorial se crea una API web que ejecuta operaciones de creación, lectura, actualización y eliminación (CRUD) en una base de datos NoSQL de MongoDB.
En este tutorial aprenderá a:
- Configuración de MongoDB
- Crear una base de datos de MongoDB
- Definir un esquema y una colección de MongoDB
- Realizar operaciones de CRUD de MongoDB desde una API web
- Personalizar la serialización de JSON
Prerequisites
- Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
- SDK de .NET 6
Configuración de MongoDB
Habilite el acceso a MongoDB y al shell de MongoDB desde cualquier lugar en el equipo de desarrollo:
En Windows, MongoDB está instalado en C:\Archivos de programa\MongoDB de forma predeterminada. Agregue C:\Archivos de programa\MongoDB\Server<número_versión>\bin a la variable de entorno
PATH.Descargue el shell de MongoDB y elija un directorio para extraerlo. Agregue la ruta de acceso resultante para
mongosh.exea la variable de entornoPATH.Elija un directorio en el equipo de desarrollo para almacenar los datos. Por ejemplo, C:\BooksData en Windows. Si no existe el directorio, créelo. El shell de mongo no crea nuevos directorios.
En el shell de comandos del sistema operativo (no en el shell de MongoDB), use el siguiente comando para conectarse a MongoDB en el puerto predeterminado 27017. Reemplace
<data_directory_path>por el directorio elegido en el paso anterior.mongod --dbpath <data_directory_path>
Use el shell de MongoDB instalado previamente en los pasos siguientes para crear una base de datos, hacer colecciones y almacenar documentos. Para más información sobre los comandos de shell de MongoDB, vea mongosh.
Abra una instancia del shell de comandos de MongoDB iniciando
mongosh.exeo ejecutando el siguiente comando en el shell de comandos:mongoshEn el shell de comandos, conéctese a la base de datos de prueba predeterminada mediante la ejecución de:
use BookStoreSe crea una base de datos denominada BookStore si aún no existe. Si la base de datos existe, su conexión se abre para las transacciones.
Cree una colección
Bookscon el comando siguiente:db.createCollection('Books')Se muestra el siguiente resultado:
{ "ok" : 1 }Defina un esquema para la colección
Bookse inserte dos documentos con el comando siguiente:db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])Se muestra un resultado similar al siguiente:
{ "acknowledged" : true, "insertedIds" : [ ObjectId("61a6058e6c43f32854e51f51"), ObjectId("61a6058e6c43f32854e51f52") ] }Note
Los objetos
ObjectIdque se muestran en el resultado anterior no coincidirán con los que se muestran en el shell de comandos.Vea los documentos en la base de datos mediante el comando siguiente:
db.Books.find().pretty()Se muestra un resultado similar al siguiente:
{ "_id" : ObjectId("61a6058e6c43f32854e51f51"), "Name" : "Design Patterns", "Price" : 54.93, "Category" : "Computers", "Author" : "Ralph Johnson" } { "_id" : ObjectId("61a6058e6c43f32854e51f52"), "Name" : "Clean Code", "Price" : 43.15, "Category" : "Computers", "Author" : "Robert C. Martin" }El esquema agrega una propiedad
_idgenerada automáticamente del tipoObjectIdpara cada documento.
Creación de un proyecto de API web de ASP.NET Core
Vaya a Archivo>Nuevo>Proyecto.
Seleccione el tipo de proyecto API web de ASP.NET Core y, luego, Siguiente.
Denomine el proyecto BookStoreApi y seleccione Siguiente.
Seleccione el marco .NET 6.0 (compatibilidad a largo plazo) y, a continuación, Crear.
En la ventana Consola del Administrador de paquetes, desplácese hasta la raíz del proyecto. Ejecute el siguiente comando para instalar el controlador .NET para MongoDB:
Install-Package MongoDB.Driver
Adición de un modelo de entidad
Agregue un directorio Modelos a la raíz del proyecto.
Agregue una clase
Bookal directorio Models con el código siguiente:using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace BookStoreApi.Models; public class Book { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } [BsonElement("Name")] public string BookName { get; set; } = null!; public decimal Price { get; set; } public string Category { get; set; } = null!; public string Author { get; set; } = null!; }En la clase anterior, se requiere la propiedad
Id:- para asignar el objeto de Common Language Runtime (CLR) a la colección de MongoDB.
- para anotarla con
[BsonId]a fin de designarla como clave principal del documento. - Anotado con
[BsonRepresentation(BsonType.ObjectId)]para permitir el paso del parámetro como tipostringen lugar de una estructura ObjectId. Mongo controla la conversión destringaObjectId.
La propiedad
BookNamese anota con el atributo[BsonElement]. El valorNamedel atributo representa el nombre de propiedad en la colección de MongoDB.
Agregar un modelo de configuración
Agregue los siguientes valores de configuración de base de datos a
appsettings.json:{ "BookStoreDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "BooksCollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }Agregue una clase
BookStoreDatabaseSettingsal directorio Models con el código siguiente:namespace BookStoreApi.Models; public class BookStoreDatabaseSettings { public string ConnectionString { get; set; } = null!; public string DatabaseName { get; set; } = null!; public string BooksCollectionName { get; set; } = null!; }La clase anterior
BookStoreDatabaseSettingsse utiliza para almacenar los valores de propiedadappsettings.jsondel archivoBookStoreDatabase. Los nombres de las propiedades de JSON y C# son nombrados idénticamente para facilitar la correspondencia.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase"));En el código anterior, la instancia de configuración a la que la sección
appsettings.jsondel archivoBookStoreDatabaseenlaza está registrada en el contenedor de inserción de dependencias (DI). Por ejemplo, una propiedadBookStoreDatabaseSettingsdel objetoConnectionStringse rellena con la propiedadBookStoreDatabase:ConnectionStringenappsettings.json.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBookStoreDatabaseSettings:using BookStoreApi.Models;
Adición de un servicio de operaciones CRUD
Agregue un directorio Servicios a la raíz del proyecto.
Agregue una clase
BooksServiceal directorio Servicios con el código siguiente:using BookStoreApi.Models; using Microsoft.Extensions.Options; using MongoDB.Driver; namespace BookStoreApi.Services; public class BooksService { private readonly IMongoCollection<Book> _booksCollection; public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); } public async Task<List<Book>> GetAsync() => await _booksCollection.Find(_ => true).ToListAsync(); public async Task<Book?> GetAsync(string id) => await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync(); public async Task CreateAsync(Book newBook) => await _booksCollection.InsertOneAsync(newBook); public async Task UpdateAsync(string id, Book updatedBook) => await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook); public async Task RemoveAsync(string id) => await _booksCollection.DeleteOneAsync(x => x.Id == id); }En el código anterior, se recuperó una instancia de
BookStoreDatabaseSettingsde la inserción de dependencias mediante la inserción de un constructor. Esta técnica proporciona acceso a los valores de configuración deappsettings.jsonque se agregaron en la sección Agregar un modelo de configuración.Agregue el código resaltado siguiente a
Program.cs:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>();En el código anterior, la clase
BooksServicese registra con inserción de dependencias para admitir la inserción del constructor en las clases de consumo. El tiempo de vida del servicio tipo singleton es el más apropiado porqueBooksServicetiene una dependencia directa deMongoClient. Según las instrucciones oficiales de reutilización de cliente Mongo,MongoClientdebe registrarse en la inserción de dependencias con una duración de servicio de tipo singleton.Agregue el código siguiente en la parte superior del archivo
Program.cspara resolver la referencia aBooksService:using BookStoreApi.Services;
La clase BooksService usa los miembros MongoDB.Driver siguientes para ejecutar operaciones CRUD en la base de datos:
MongoClient: lee la instancia del servidor para ejecutar operaciones de base de datos. Se proporciona la cadena de conexión de MongoDB al constructor de esta clase:
public BooksService( IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.BooksCollectionName); }IMongoDatabase: representa la base de datos de Mongo para ejecutar operaciones. Este tutorial usa el método genérico GetCollection<TDocument>(collection) en la interfaz para tener acceso a los datos de una colección específica. Ejecute las operaciones CRUD en la colección después de llamar a este método. En la llamada al método
GetCollection<TDocument>(collection):-
collectionrepresenta el nombre de la colección. -
TDocumentrepresenta el tipo de objeto CLR almacenado en la colección.
-
GetCollection<TDocument>(collection) devuelve un objeto MongoCollection que representa la colección. En este tutorial, se invocan los métodos siguientes en la colección:
- DeleteOneAsync: elimina un único documento que cumpla los criterios de búsqueda proporcionados.
- Find<TDocument>: devuelve todos los documentos de la colección que cumplen los criterios de búsqueda indicados.
- InsertOneAsync: inserta el objeto proporcionado como un nuevo documento en la colección.
- ReplaceOneAsync: reemplaza un único documento que cumpla los criterios de búsqueda indicados por el objeto proporcionado.
Incorporación de un controlador
Agregue una clase BooksController al directorio Controllers con el código siguiente:
using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace BookStoreApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
private readonly BooksService _booksService;
public BooksController(BooksService booksService) =>
_booksService = booksService;
[HttpGet]
public async Task<List<Book>> Get() =>
await _booksService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<Book>> Get(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
return book;
}
[HttpPost]
public async Task<IActionResult> Post(Book newBook)
{
await _booksService.CreateAsync(newBook);
return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
}
[HttpPut("{id:length(24)}")]
public async Task<IActionResult> Update(string id, Book updatedBook)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
updatedBook.Id = book.Id;
await _booksService.UpdateAsync(id, updatedBook);
return NoContent();
}
[HttpDelete("{id:length(24)}")]
public async Task<IActionResult> Delete(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
{
return NotFound();
}
await _booksService.RemoveAsync(id);
return NoContent();
}
}
El controlador de API web anterior:
- Usa la clase
BooksServicepara ejecutar operaciones CRUD. - Contiene métodos de acción para admitir las solicitudes GET, POST, PUT y DELETE de HTTP.
- Llama a CreatedAtAction en el método de acción
Createpara devolver una respuesta HTTP 201. El código de estado 201 es la respuesta estándar para un método HTTP POST que crea un recurso en el servidor.CreatedAtActiontambién agrega un encabezadoLocationa la respuesta. El encabezadoLocationespecifica el identificador URI del libro recién creado.
Prueba de la API web
Compile y ejecute la aplicación.
Vaya a
https://localhost:<port>/api/books, donde<port>es el número de puerto asignado automáticamente para la aplicación, para probar el método de acciónGetsin parámetros del controlador. Se muestra una respuesta JSON similar a la siguiente:[ { "id": "61a6058e6c43f32854e51f51", "bookName": "Design Patterns", "price": 54.93, "category": "Computers", "author": "Ralph Johnson" }, { "id": "61a6058e6c43f32854e51f52", "bookName": "Clean Code", "price": 43.15, "category": "Computers", "author": "Robert C. Martin" } ]Vaya a
https://localhost:<port>/api/books/{id here}para probar el método de acciónGetsobrecargado del controlador. Se muestra una respuesta JSON similar a la siguiente:{ "id": "61a6058e6c43f32854e51f52", "bookName": "Clean Code", "price": 43.15, "category": "Computers", "author": "Robert C. Martin" }
Configuración de las opciones de serialización de JSON
Hay dos detalles que cambiar sobre las respuestas JSON devueltas en la sección Prueba de la API web:
- Las mayúsculas y minúsculas Camel predeterminadas de los nombres de propiedad se deben cambiar para que coincidan con el uso de mayúsculas y minúsculas de Pascal de los nombres de propiedad del objeto CLR.
- La propiedad
bookNamese debe devolver comoName.
Para satisfacer los requisitos anteriores, realice los cambios siguientes:
En
Program.cs, encadena el siguiente código resaltado a la llamada al métodoAddControllers:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.Configure<BookStoreDatabaseSettings>( builder.Configuration.GetSection("BookStoreDatabase")); builder.Services.AddSingleton<BooksService>(); builder.Services.AddControllers() .AddJsonOptions( options => options.JsonSerializerOptions.PropertyNamingPolicy = null);Con el cambio anterior, los nombres de propiedad de la respuesta JSON serializada de la API web coinciden con sus nombres de propiedad correspondientes en el tipo de objeto CLR. Por ejemplo, la propiedad
Bookde la claseAuthorse serializa comoAuthor, en lugar deauthor.En
Models/Book.cs, anote la propiedadBookNamecon el atributo[JsonPropertyName]:[BsonElement("Name")] [JsonPropertyName("Name")] public string BookName { get; set; } = null!;El valor
[JsonPropertyName]del atributoNamerepresenta el nombre de propiedad en la respuesta JSON serializada de la API web.Agregue el código siguiente en la parte superior de
Models/Book.cspara resolver la referencia al atributo[JsonProperty]:using System.Text.Json.Serialization;Repita los pasos definidos en la sección Prueba de la API web. Observe la diferencia en los nombres de propiedad JSON.
Agregar compatibilidad con la autenticación a una API web
ASP.NET Core Identity agrega la funcionalidad de inicio de sesión de la interfaz de usuario (IU) a las aplicaciones web de ASP.NET Core. Para proteger las API web y las SPA, usa una de las siguientes opciones:
Duende Identity Server es un marco de OpenID Connect y OAuth 2.0 para ASP.NET Core. Duende Identity Server permite las siguientes características de seguridad:
- Autenticación como servicio (AaaS)
- Inicio de sesión único (SSO) mediante varios tipos de aplicaciones
- Control de acceso para API
- Puerta de enlace de federación
Important
Duende Software puede requerir que pagues una tarifa de licencia para el uso en producción de Duende Identity Server. Para obtener más información, consulte Migración de ASP.NET Core en .NET 5 a .NET 6.
Para más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Recursos adicionales
En este tutorial se crea una API web que ejecuta operaciones de creación, lectura, actualización y eliminación (CRUD) en una base de datos NoSQL de MongoDB.
En este tutorial aprenderá a:
- Configuración de MongoDB
- Crear una base de datos de MongoDB
- Definir un esquema y una colección de MongoDB
- Realizar operaciones de CRUD de MongoDB desde una API web
- Personalizar la serialización de JSON
Vea o descargue el código de ejemplo (cómo descargarlo)
Prerequisites
- .NET Core SDK 3.0 o posterior
- Visual Studio 2019 con la carga de trabajo ASP.NET y desarrollo web.
- MongoDB
Configuración de MongoDB
Si usa Windows, MongoDB está instalado en C:\Archivos de programa\MongoDB de forma predeterminada. Agregue C:\Archivos de programa\MongoDB\Server<número_versión>\bin a la variable de entorno Path. Este cambio permite el acceso a MongoDB desde cualquier lugar en el equipo de desarrollo.
Use el Shell de mongo en los pasos siguientes para crear una base de datos, hacer colecciones y almacenar documentos. Para obtener más información sobre los comandos de Shell de mongo, consulte Working with the mongo Shell (Trabajo con el shell de Mongo).
Elija un directorio en el equipo de desarrollo para almacenar los datos. Por ejemplo, C:\BooksData en Windows. Si no existe el directorio, créelo. El shell de mongo no crea nuevos directorios.
Abra un shell de comandos. Ejecute el comando siguiente para conectarse a MongoDB en el puerto predeterminado 27017. No olvide reemplazar
<data_directory_path>por el directorio que eligió en el paso anterior.mongod --dbpath <data_directory_path>Abra otra instancia del shell de comandos. Conéctese a la base de datos de prueba de forma predeterminada ejecutando el comando siguiente:
mongoEjecute el comando siguiente en un shell de comandos:
use BookstoreDbSe crea una base de datos denominada BookstoreDb si aún no existe. Si la base de datos existe, su conexión se abre para las transacciones.
Cree una colección
Bookscon el comando siguiente:db.createCollection('Books')Se muestra el siguiente resultado:
{ "ok" : 1 }Defina un esquema para la colección
Bookse inserte dos documentos con el comando siguiente:db.Books.insertMany([{'Name':'Design Patterns','Price':54.93,'Category':'Computers','Author':'Ralph Johnson'}, {'Name':'Clean Code','Price':43.15,'Category':'Computers','Author':'Robert C. Martin'}])Se muestra el siguiente resultado:
{ "acknowledged" : true, "insertedIds" : [ ObjectId("5bfd996f7b8e48dc15ff215d"), ObjectId("5bfd996f7b8e48dc15ff215e") ] }Note
Los identificadores que se muestran en este artículo no coinciden con los que se mostrarán cuando ejecute este ejemplo.
Vea los documentos en la base de datos mediante el comando siguiente:
db.Books.find({}).pretty()Se muestra el siguiente resultado:
{ "_id" : ObjectId("5bfd996f7b8e48dc15ff215d"), "Name" : "Design Patterns", "Price" : 54.93, "Category" : "Computers", "Author" : "Ralph Johnson" } { "_id" : ObjectId("5bfd996f7b8e48dc15ff215e"), "Name" : "Clean Code", "Price" : 43.15, "Category" : "Computers", "Author" : "Robert C. Martin" }El esquema agrega una propiedad
_idgenerada automáticamente del tipoObjectIdpara cada documento.
La base de datos está lista. Puede empezar a crear la API web de ASP.NET Core.
Creación de un proyecto de API web de ASP.NET Core
Vaya a Archivo>Nuevo>Proyecto.
Seleccione el tipo de proyecto Aplicación web de ASP.NET Core y, luego, Siguiente.
Denomine el proyecto BooksApi y seleccione Crear.
Seleccione .NET Core como plataforma de destino y ASP.NET Core 3.0. Seleccione la plantilla de proyecto API y, luego, Crear.
Visite la galería de NuGet: MongoDB.Driver para determinar la última versión estable del controlador .NET para MongoDB. En la ventana Consola del Administrador de paquetes, desplácese hasta la raíz del proyecto. Ejecute el siguiente comando para instalar el controlador .NET para MongoDB:
Install-Package MongoDB.Driver -Version {VERSION}
Adición de un modelo de entidad
Agregue un directorio Modelos a la raíz del proyecto.
Agregue una clase
Bookal directorio Models con el código siguiente:using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace BooksApi.Models { public class Book { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } [BsonElement("Name")] public string BookName { get; set; } public decimal Price { get; set; } public string Category { get; set; } public string Author { get; set; } } }En la clase anterior, se requiere la propiedad
Id:- para asignar el objeto de Common Language Runtime (CLR) a la colección de MongoDB.
- para anotarla con
[BsonId]a fin de designarla como clave principal del documento. - Anotado con
[BsonRepresentation(BsonType.ObjectId)]para permitir el paso del parámetro como tipostringen lugar de una estructura ObjectId. Mongo controla la conversión destringaObjectId.
La propiedad
BookNamese anota con el atributo[BsonElement]. El valorNamedel atributo representa el nombre de propiedad en la colección de MongoDB.
Agregar un modelo de configuración
Agregue los siguientes valores de configuración de base de datos a
appsettings.json:{ "BookstoreDatabaseSettings": { "BooksCollectionName": "Books", "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookstoreDb" }, "Logging": { "IncludeScopes": false, "Debug": { "LogLevel": { "Default": "Warning" } }, "Console": { "LogLevel": { "Default": "Warning" } } } }Agregue un archivo
BookstoreDatabaseSettings.csal directorio Models con el código siguiente:namespace BooksApi.Models { public class BookstoreDatabaseSettings : IBookstoreDatabaseSettings { public string BooksCollectionName { get; set; } public string ConnectionString { get; set; } public string DatabaseName { get; set; } } public interface IBookstoreDatabaseSettings { string BooksCollectionName { get; set; } string ConnectionString { get; set; } string DatabaseName { get; set; } } }La clase anterior
BookstoreDatabaseSettingsse utiliza para almacenar los valores de propiedadappsettings.jsondel archivoBookstoreDatabaseSettings. Los nombres de las propiedades de JSON y C# son nombrados idénticamente para facilitar la correspondencia.Agregue el código resaltado siguiente a
Startup.ConfigureServices:public void ConfigureServices(IServiceCollection services) { // requires using Microsoft.Extensions.Options services.Configure<BookstoreDatabaseSettings>( Configuration.GetSection(nameof(BookstoreDatabaseSettings))); services.AddSingleton<IBookstoreDatabaseSettings>(sp => sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value); services.AddControllers(); }En el código anterior:
- La instancia de configuración a la que la sección
appsettings.jsondel archivoBookstoreDatabaseSettingsenlaza está registrada en el contenedor de inserción de dependencias (DI). Por ejemplo, una propiedadBookstoreDatabaseSettingsdel objetoConnectionStringse rellena con la propiedadBookstoreDatabaseSettings:ConnectionStringenappsettings.json. - La interfaz
IBookstoreDatabaseSettingsse registra en la inserción de dependencias con una duración de servicio de tipo singleton. Cuando se inserta, la instancia de la interfaz se resuelve en un objetoBookstoreDatabaseSettings.
- La instancia de configuración a la que la sección
Agregue el código siguiente en la parte superior del archivo
Startup.cspara resolver las referencia aBookstoreDatabaseSettingsyIBookstoreDatabaseSettings:using BooksApi.Models;
Adición de un servicio de operaciones CRUD
Agregue un directorio Servicios a la raíz del proyecto.
Agregue una clase
BookServiceal directorio Servicios con el código siguiente:using BooksApi.Models; using MongoDB.Driver; using System.Collections.Generic; using System.Linq; namespace BooksApi.Services { public class BookService { private readonly IMongoCollection<Book> _books; public BookService(IBookstoreDatabaseSettings settings) { var client = new MongoClient(settings.ConnectionString); var database = client.GetDatabase(settings.DatabaseName); _books = database.GetCollection<Book>(settings.BooksCollectionName); } public List<Book> Get() => _books.Find(book => true).ToList(); public Book Get(string id) => _books.Find<Book>(book => book.Id == id).FirstOrDefault(); public Book Create(Book book) { _books.InsertOne(book); return book; } public void Update(string id, Book bookIn) => _books.ReplaceOne(book => book.Id == id, bookIn); public void Remove(Book bookIn) => _books.DeleteOne(book => book.Id == bookIn.Id); public void Remove(string id) => _books.DeleteOne(book => book.Id == id); } }En el código anterior, se recuperó una instancia de
IBookstoreDatabaseSettingsde la inserción de dependencias mediante la inserción de un constructor. Esta técnica proporciona acceso a los valores de configuración deappsettings.jsonque se agregaron en la sección Agregar un modelo de configuración.Agregue el código resaltado siguiente a
Startup.ConfigureServices:public void ConfigureServices(IServiceCollection services) { services.Configure<BookstoreDatabaseSettings>( Configuration.GetSection(nameof(BookstoreDatabaseSettings))); services.AddSingleton<IBookstoreDatabaseSettings>(sp => sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value); services.AddSingleton<BookService>(); services.AddControllers(); }En el código anterior, la clase
BookServicese registra con inserción de dependencias para admitir la inserción del constructor en las clases de consumo. El tiempo de vida del servicio tipo singleton es el más apropiado porqueBookServicetiene una dependencia directa deMongoClient. Según las instrucciones oficiales de reutilización de cliente Mongo,MongoClientdebe registrarse en la inserción de dependencias con una duración de servicio de tipo singleton.Agregue el código siguiente en la parte superior del archivo
Startup.cspara resolver la referencia aBookService:using BooksApi.Services;
La clase BookService usa los miembros MongoDB.Driver siguientes para ejecutar operaciones CRUD en la base de datos:
MongoClient: lee la instancia del servidor para ejecutar operaciones de base de datos. Se proporciona la cadena de conexión de MongoDB al constructor de esta clase:
public BookService(IBookstoreDatabaseSettings settings) { var client = new MongoClient(settings.ConnectionString); var database = client.GetDatabase(settings.DatabaseName); _books = database.GetCollection<Book>(settings.BooksCollectionName); }IMongoDatabase: representa la base de datos de Mongo para ejecutar operaciones. Este tutorial usa el método genérico GetCollection<TDocument>(collection) en la interfaz para tener acceso a los datos de una colección específica. Ejecute las operaciones CRUD en la colección después de llamar a este método. En la llamada al método
GetCollection<TDocument>(collection):-
collectionrepresenta el nombre de la colección. -
TDocumentrepresenta el tipo de objeto CLR almacenado en la colección.
-
GetCollection<TDocument>(collection) devuelve un objeto MongoCollection que representa la colección. En este tutorial, se invocan los métodos siguientes en la colección:
- DeleteOne: elimina un único documento que cumple los criterios de búsqueda proporcionados.
- Find<TDocument>: devuelve todos los documentos de la colección que cumplen los criterios de búsqueda indicados.
- InsertOne: inserta el objeto proporcionado como un nuevo documento en la colección.
- ReplaceOne: reemplaza un único documento que cumple los criterios de búsqueda indicados por el objeto proporcionado.
Incorporación de un controlador
Agregue una clase BooksController al directorio Controllers con el código siguiente:
using BooksApi.Models;
using BooksApi.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace BooksApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{
private readonly BookService _bookService;
public BooksController(BookService bookService)
{
_bookService = bookService;
}
[HttpGet]
public ActionResult<List<Book>> Get() =>
_bookService.Get();
[HttpGet("{id:length(24)}", Name = "GetBook")]
public ActionResult<Book> Get(string id)
{
var book = _bookService.Get(id);
if (book == null)
{
return NotFound();
}
return book;
}
[HttpPost]
public ActionResult<Book> Create(Book book)
{
_bookService.Create(book);
return CreatedAtRoute("GetBook", new { id = book.Id.ToString() }, book);
}
[HttpPut("{id:length(24)}")]
public IActionResult Update(string id, Book bookIn)
{
var book = _bookService.Get(id);
if (book == null)
{
return NotFound();
}
_bookService.Update(id, bookIn);
return NoContent();
}
[HttpDelete("{id:length(24)}")]
public IActionResult Delete(string id)
{
var book = _bookService.Get(id);
if (book == null)
{
return NotFound();
}
_bookService.Remove(id);
return NoContent();
}
}
}
El controlador de API web anterior:
- Usa la clase
BookServicepara ejecutar operaciones CRUD. - Contiene métodos de acción para admitir las solicitudes GET, POST, PUT y DELETE de HTTP.
- Llama a CreatedAtRoute en el método de acción
Createpara devolver una respuesta HTTP 201. El código de estado 201 es la respuesta estándar para un método HTTP POST que crea un recurso en el servidor.CreatedAtRoutetambién agrega un encabezadoLocationa la respuesta. El encabezadoLocationespecifica el identificador URI del libro recién creado.
Prueba de la API web
Compile y ejecute la aplicación.
Vaya a
https://localhost:<port>/api/bookspara probar el método de acciónGetsin parámetros del controlador. Se muestra la siguiente respuesta JSON:[ { "id":"5bfd996f7b8e48dc15ff215d", "bookName":"Design Patterns", "price":54.93, "category":"Computers", "author":"Ralph Johnson" }, { "id":"5bfd996f7b8e48dc15ff215e", "bookName":"Clean Code", "price":43.15, "category":"Computers", "author":"Robert C. Martin" } ]Vaya a
https://localhost:<port>/api/books/{id here}para probar el método de acciónGetsobrecargado del controlador. Se muestra la siguiente respuesta JSON:{ "id":"{ID}", "bookName":"Clean Code", "price":43.15, "category":"Computers", "author":"Robert C. Martin" }
Configuración de las opciones de serialización de JSON
Hay dos detalles que cambiar sobre las respuestas JSON devueltas en la sección Prueba de la API web:
- Las mayúsculas y minúsculas Camel predeterminadas de los nombres de propiedad se deben cambiar para que coincidan con el uso de mayúsculas y minúsculas de Pascal de los nombres de propiedad del objeto CLR.
- La propiedad
bookNamese debe devolver comoName.
Para satisfacer los requisitos anteriores, realice los cambios siguientes:
JSON.NET se ha quitado de la plataforma compartida de ASP.NET. Agregue una referencia de paquete a
Microsoft.AspNetCore.Mvc.NewtonsoftJson.En
Startup.ConfigureServices, encadena el siguiente código resaltado a la llamada al métodoAddControllers:public void ConfigureServices(IServiceCollection services) { services.Configure<BookstoreDatabaseSettings>( Configuration.GetSection(nameof(BookstoreDatabaseSettings))); services.AddSingleton<IBookstoreDatabaseSettings>(sp => sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value); services.AddSingleton<BookService>(); services.AddControllers() .AddNewtonsoftJson(options => options.UseMemberCasing()); }Con el cambio anterior, los nombres de propiedad de la respuesta JSON serializada de la API web coinciden con sus nombres de propiedad correspondientes en el tipo de objeto CLR. Por ejemplo, la propiedad
Bookde la claseAuthorse serializa comoAuthor.En
Models/Book.cs, anote la propiedadBookNamecon el siguiente atributo[JsonProperty]:[BsonElement("Name")] [JsonProperty("Name")] public string BookName { get; set; }El valor
[JsonProperty]del atributoNamerepresenta el nombre de propiedad en la respuesta JSON serializada de la API web.Agregue el código siguiente en la parte superior de
Models/Book.cspara resolver la referencia al atributo[JsonProperty]:using Newtonsoft.Json;Repita los pasos definidos en la sección Prueba de la API web. Observe la diferencia en los nombres de propiedad JSON.
Agregar compatibilidad con la autenticación a una API web
ASP.NET Core Identity agrega la funcionalidad de inicio de sesión de la interfaz de usuario (IU) a las aplicaciones web de ASP.NET Core. Para proteger las API web y las SPA, usa una de las siguientes opciones:
- Microsoft Entra ID
- Duende IdentityServer. Duende IdentityServer es un producto de terceros.
Duende IdentityServer es un marco de OpenID Connect y OAuth 2.0 para ASP.NET Core. Duende IdentityServer habilita las siguientes características de seguridad:
- Autenticación como servicio (AaaS)
- Inicio de sesión único (SSO) mediante varios tipos de aplicaciones
- Control de acceso para API
- Puerta de enlace de federación
Para más información, consulta Información general de Duende IdentityServer.
Para más información sobre otros proveedores de autenticación, consulte Opciones de autenticación de OSS de la comunidad para ASP.NET Core.
Pasos siguientes
Para obtener más información sobre la creación de las API web de ASP.NET Core, consulte los siguientes recursos: