Tutorial: Creación de una API web con ASP.NET Core
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
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 9 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Por Rick Anderson y Kirk Larkin
En este tutorial se enseñan los conceptos básicos de la creación de una API web basada en controlador que usa una base de datos. Otro enfoque para crear API en ASP.NET Core consiste en crear API mínimas. Para obtener ayuda para elegir entre las API mínimas y las API basadas en controlador, consulte Introducción a las API. Para ver un tutorial sobre cómo crear una API mínima, consulte Tutorial: Creación de una API mínima con ASP.NET Core.
Información general
En este tutorial se crea la siguiente API:
API | Descripción | Cuerpo de la solicitud | Cuerpo de la respuesta |
---|---|---|---|
GET /api/todoitems |
Obtener todas las tareas pendientes | None | Matriz de tareas pendientes |
GET /api/todoitems/{id} |
Obtener un elemento por identificador | None | Tarea pendiente |
POST /api/todoitems |
Incorporación de un nuevo elemento | Tarea pendiente | Tarea pendiente |
PUT /api/todoitems/{id} |
Actualizar un elemento existente | Tarea pendiente | None |
DELETE /api/todoitems/{id} |
Eliminar un elemento | None | None |
En el diagrama siguiente, se muestra el diseño de la aplicación.
Requisitos previos
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Creación de un proyecto web
- En el menú Archivo, seleccione Nuevo>Proyecto.
- Escriba API web en el cuadro de búsqueda.
- Seleccione la plantilla API web de ASP.NET Core y seleccione Siguiente.
- En el cuadro de diálogo Configurar el nuevo proyecto, asigne al proyecto el nombre TodoApi y seleccione Siguiente.
- En el cuadro de diálogo Información adicional:
- Confirme que el Marco es .NET 8.0 (Compatibilidad a largo plazo).
- Confirme que la casilla Use controllers(uncheck to use minimal APIs) [Usar controladores (desactivar para usar API mínimas)] está activada.
- Confirme que la casilla Habilitar compatibilidad con OpenAPI está activada.
- Seleccione Crear.
Adición de un paquete NuGet
Se debe agregar un paquete NuGet para admitir la base de datos que se usa en este tutorial.
- En el menú Herramientas, seleccione Administrador de paquetes NuGet > Administrar paquetes NuGet para la solución.
- Seleccione la pestaña Examinar.
- Escriba Microsoft.EntityFrameworkCore.InMemory en el cuadro de búsqueda y, después, seleccione
Microsoft.EntityFrameworkCore.InMemory
. - Active la casilla Proyecto en el panel derecho y, después, seleccione Instalar.
Nota:
Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulta los artículos de Instalación y administración de paquetes en Flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.
Prueba del proyecto
La plantilla de proyecto crea una API WeatherForecast
compatible con Swagger.
Presione Ctrl+F5 para ejecutarla sin el depurador.
Visual Studio muestra el siguiente cuadro de diálogo cuando un proyecto aún no está configurado para usar SSL:
Haga clic en Sí si confía en el certificado SSL de IIS Express.
Se muestra el cuadro de diálogo siguiente:
Si acepta confiar en el certificado de desarrollo, seleccione Sí.
Para obtener información sobre cómo confiar en el explorador Firefox, consulte Error de certificado SEC_ERROR_INADEQUATE_KEY_USAGE de Firefox.
Visual Studio inicia el explorador predeterminado y navega hasta https://localhost:<port>/swagger/index.html
, donde <port>
es un número de puerto elegido aleatoriamente en la creación del proyecto.
Se abre la página de Swagger /swagger/index.html
. Seleccione GET>Try it out>Execute (GET > Probar > Ejecutar). La página muestra lo siguiente:
- Comando de Curl para probar la API WeatherForecast
- Dirección URL para probar la API WeatherForecast
- Código de respuesta, cuerpo y encabezados
- Un cuadro de lista desplegable con los tipos de elementos multimedia y el esquema y valor de ejemplo.
Si no aparece la página Swagger, consulte este problema de GitHub.
Swagger se usa para generar una documentación útil y páginas de ayuda para API web. En este tutorial se usa Swagger para probar la aplicación. Para más información sobre Swagger, vea Documentación de la API web de ASP.NET Core con Swagger/OpenAPI.
Copie y pegue la URL de solicitud en el explorador: https://localhost:<port>/weatherforecast
.
Se devuelve un JSON similar al siguiente ejemplo:
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
Incorporación de una clase de modelo
Un modelo es un conjunto de clases que representan los datos que la aplicación administra. El modelo para esta aplicación es la clase TodoItem
.
- En el Explorador de soluciones, haga clic con el botón derecho en el proyecto. Seleccione Agregar>Nueva carpeta. Asigne a la carpeta el nombre
Models
. - Haga clic con el botón derecho en la carpeta
Models
y seleccione Agregar>Clase. Asigne a la clase el nombre TodoItem y seleccione Agregar. - Reemplace el código de plantilla por lo siguiente:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
La propiedad Id
funciona como clave única en una base de datos relacional.
Las clases de modelo pueden ir en cualquier lugar del proyecto, pero, por convención, se usa la carpeta Models
.
Incorporación de un contexto de base de datos
El contexto de base de datos es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos. Esta clase se crea derivándola de la clase Microsoft.EntityFrameworkCore.DbContext.
- Haga clic con el botón derecho en la carpeta
Models
y seleccione Agregar>Clase. Asigne a la clase el nombre TodoContext y haga clic en Agregar.
Escriba el siguiente código:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
Registro del contexto de base de datos
En ASP.NET Core, los servicios (como el contexto de la base de datos) deben registrarse con el contenedor de inserción de dependencias (DI). El contenedor proporciona el servicio a los controladores.
Actualice Program.cs
con el siguiente código resaltado:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
El código anterior:
- Agrega directivas
using
. - Agrega el contexto de base de datos para el contenedor de DI.
- Especifica que el contexto de base de datos usará una base de datos en memoria.
Scaffolding de un controlador
Haga clic con el botón derecho en la carpeta
Controllers
.Seleccione Agregar>New Scaffolded Item.
Seleccione Controlador de API con acciones mediante Entity Framework y, después, seleccione Agregar.
En el cuadro de diálogo Add API Controller with actions, using Entity Framework (Agregar controlador de API con acciones mediante Entity Framework):
- Seleccione TodoItem (TodoApi.Models) en Clase de modelo.
- Seleccione TodoContext (TodoApi.Models) en Clase de contexto de datos.
- Seleccione Agregar.
Si se produce un error en la operación de scaffolding, seleccione Agregar para probar el scaffolding una segunda vez.
El código generado:
- Marca la clase con el atributo
[ApiController]
. Este atributo indica que el controlador responde a las solicitudes de la API web. Para información sobre comportamientos específicos que permite el atributo, vea Creación de una API web con ASP.NET Core. - Utiliza la inserción de dependencias para insertar el contexto de base de datos (
TodoContext
) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.
Las plantillas de ASP.NET Core para:
- Controladores con vistas incluyen
[action]
en la plantilla de ruta. - Controladores de API no incluyen
[action]
en la plantilla de ruta.
Cuando el token [action]
no está en la plantilla de ruta, el nombre de la acción (nombre del método) no se incluye en el punto de conexión. Es decir, el nombre del método asociado a la acción no se usa en la ruta coincidente.
Actualización del método create de PostTodoItem
Actualice la instrucción "return" en PostTodoItem
para usar el operador nameof:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
El código anterior es un método HTTP POST
, indicado por el atributo [HttpPost]
. El método obtiene el valor de TodoItem
del cuerpo de la solicitud HTTP.
Para más información, vea Enrutamiento mediante atributos con atributos Http[Verb].
El método CreatedAtAction realiza las acciones siguientes:
- Devuelve un código de estado HTTP 201 cuando se ha ejecutado correctamente.
HTTP 201
es la respuesta estándar de un métodoHTTP POST
que crea un recurso en el servidor. - Agrega un encabezado Location a la respuesta. El encabezado
Location
especifica el URI de la tarea pendiente recién creada. Para obtener más información, consulte 10.2.2 201 creado. - Hace referencia a la acción
GetTodoItem
para crear el identificador URI del encabezadoLocation
. La palabra clavenameof
de C# se usa para evitar que se codifique de forma rígida el nombre de acción en la llamada aCreatedAtAction
.
Prueba de PostTodoItem
Presione Ctrl+F5 para ejecutar la aplicación.
En la ventana del explorador Swagger, seleccione POST /api/TodoItems y, a continuación, seleccione Probarlo.
En la ventana Cuerpo de la solicitud, actualiza JSON. Por ejemplo,
{ "name": "walk dog", "isComplete": true }
Seleccione Ejecutar.
Prueba del URI del encabezado de ubicación
En el POST anterior, la interfaz de usuario de Swagger muestra el encabezado de ubicación en Encabezados de respuesta. Por ejemplo, location: https://localhost:7260/api/TodoItems/1
. El encabezado de ubicación muestra el URI al recurso creado.
Para probar el encabezado de ubicación:
En la ventana del explorador Swagger, seleccione GET /api/TodoItems/{id} y, a continuación, seleccione Probarlo.
Escriba
1
en el cuadro de entradaid
y, a continuación, seleccione Ejecutar.
Examen de los métodos GET
Se implementan dos puntos de conexión GET:
GET /api/todoitems
GET /api/todoitems/{id}
En la sección anterior se muestra un ejemplo de la ruta /api/todoitems/{id}
.
Siga las instrucciones POST para agregar otro elemento de tareas pendientes y, a continuación, pruebe la ruta /api/todoitems
mediante Swagger.
Esta aplicación utiliza una base de datos en memoria. Si la aplicación se detiene e inicia, la solicitud GET anterior no devuelve ningún dato. Si no se devuelve ningún dato, publique los datos en la aplicación con POST.
Enrutamiento y rutas URL
El atributo [HttpGet]
indica un método que responde a una solicitud HTTP GET
. La ruta de dirección URL para cada método se construye como sigue:
Comience por la cadena de plantilla en el atributo
Route
del controlador:[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
Reemplace
[controller]
por el nombre del controlador, que convencionalmente es el nombre de clase de controlador sin el sufijo "Controller". En este ejemplo, el nombre de clase de controlador es TodoItemsController; por tanto, el nombre del controlador es "TodoItems". El enrutamiento en ASP.NET Core no distingue entre mayúsculas y minúsculas.Si el atributo
[HttpGet]
tiene una plantilla de ruta (por ejemplo,[HttpGet("products")]
), anéxela a la ruta de acceso. En este ejemplo no se usa una plantilla. Para más información, vea Enrutamiento mediante atributos con atributos Http[Verb].
En el siguiente método GetTodoItem
, "{id}"
es una variable de marcador de posición correspondiente al identificador único de la tarea pendiente. Al invocar a GetTodoItem
, el valor "{id}"
de la URL se proporciona al método en su parámetro id
.
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
Valores devueltos
El tipo de valor devuelto de los métodos GetTodoItems
y GetTodoItem
es ActionResult<T> type. ASP.NET Core serializa automáticamente el objeto a JSON y escribe el JSON en el cuerpo del mensaje de respuesta. El código de respuesta de este tipo de valor devuelto es 200 OK, suponiendo que no haya ninguna excepción no controlada. Las excepciones no controladas se convierten en errores 5xx.
Los tipos de valores devueltos ActionResult
pueden representar una gama amplia de códigos de estado HTTP. Por ejemplo, GetTodoItem
puede devolver dos valores de estado diferentes:
- Si no hay ningún elemento que coincida con el identificador solicitado, el método devolverá un código de error de estado 404 NotFound.
- En caso contrario, el método devuelve 200 con un cuerpo de respuesta JSON. La devolución de
item
genera una respuestaHTTP 200
.
Método PutTodoItem
Examine el método PutTodoItem
:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
es similar a PostTodoItem
, salvo por el hecho de que usa HTTP PUT
. La respuesta es 204 Sin contenido. Según la especificación HTTP, una solicitud PUT
requiere que el cliente envíe toda la entidad actualizada, no solo los cambios. Para admitir actualizaciones parciales, use HTTP PATCH.
Prueba del método PutTodoItem
En este ejemplo se usa una base de datos en memoria que se debe inicializar cada vez que se inicia la aplicación. Debe haber un elemento en la base de datos antes de que realice una llamada PUT. Llame a GET para asegurarse de que hay un elemento en la base de datos antes de realizar una llamada PUT.
Con la interfaz de usuario de Swagger, use el botón PUT para actualizar el valor TodoItem
que tiene el identificador = 1 y establezca su nombre en "feed fish"
. Observe que la respuesta es HTTP 204 No Content
.
Método DeleteTodoItem
Examine el método DeleteTodoItem
:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
Prueba del método DeleteTodoItem
Use la interfaz de usuario de Swagger para eliminar el valor TodoItem
que tiene el identificador = 1. Observe que la respuesta es HTTP 204 No Content
.
Prueba con otras herramientas
Hay muchas otras herramientas que se pueden usar para probar las API web, por ejemplo:
- Explorador de puntos de conexión de Visual Studio y archivos .http
- http-repl
- curl. Swagger usa
curl
y muestra el comandocurl
que envió. - Fiddler
Para más información, consulte:
- Tutorial de API mínima: prueba con archivos .http y el Explorador de puntos de conexión
- Instalación y prueba de API con
http-repl
Prevención del exceso de publicación
Actualmente, la aplicación de ejemplo expone todo el objeto TodoItem
. Las aplicaciones de producción suelen limitar los datos que se escriben y se devuelven mediante un subconjunto del modelo. Hay varias razones para ello y la seguridad es una de las principales. El subconjunto de un modelo se suele conocer como un objeto de transferencia de datos (DTO), modelo de entrada o modelo de vista. En este tutorial, se usa DTO.
Se puede usar un DTO para:
- Evitar el exceso de publicación.
- Ocultar las propiedades que los clientes no deben ver.
- Omitir algunas propiedades para reducir el tamaño de la carga.
- Acoplar los gráficos de objetos que contienen objetos anidados. Los gráficos de objetos acoplados pueden ser más cómodos para los clientes.
Para mostrar el enfoque del DTO, actualice la clase TodoItem
a fin de que incluya un campo secreto:
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
El campo secreto debe ocultarse en esta aplicación, pero una aplicación administrativa podría decidir exponerlo.
Compruebe que puede publicar y obtener el campo secreto.
Cree un modelo de DTO:
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Actualice el valor de TodoItemsController
para usar TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
Compruebe que no puede publicar ni obtener el campo secreto.
Llamar a la API web con JavaScript
Consulte Tutorial: Llamada a una API web de ASP.NET Core con JavaScript.
Serie de vídeos de API web
Vea Vídeo: Serie para principiantes: API web.
Patrones de aplicación web confiable
Consulte El patrón de aplicación web confiable para .NET vídeos de YouTube y artículo para obtener una guía sobre cómo crear una aplicación moderna, confiable, eficaz, comprobable, rentable y escalable ASP.NET Core, ya sea desde cero o refactorizando una aplicación existente.
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
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
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
- Federation Gateway
Importante
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, consulta Migración de ASP.NET Core 5.0 a 6.0.
Para más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Publicar en Azure
Para obtener más información sobre la implementación en Azure, consulte Inicio rápido: Implementación de una aplicación web de ASP.NET.
Recursos adicionales
Vea o descargue el código de ejemplo para este tutorial. Vea cómo descargarlo.
Para obtener más información, vea los siguientes recursos:
- Creación de API web con ASP.NET Core
- Tutorial: Creación de una API mínima con ASP.NET Core
- Documentación de la API web de ASP.NET Core con Swagger/OpenAPI
- Razor Pages con Entity Framework Core en ASP.NET Core: Tutorial 1 de 8
- Enrutar a acciones de controlador de ASP.NET Core
- Tipos de valor devuelto de acción del controlador en la API web de ASP.NET Core
- Implementar aplicaciones de ASP.NET Core en Azure App Service
- Hospedaje e implementación de ASP.NET Core
- Creación de una API web con ASP.NET Core
En este tutorial se enseñan los conceptos básicos de la creación de una API web basada en controlador que usa una base de datos. Otro enfoque para crear API en ASP.NET Core consiste en crear API mínimas. Para obtener ayuda para elegir entre las API mínimas y las API basadas en controlador, consulte Introducción a las API. Para ver un tutorial sobre cómo crear una API mínima, consulte Tutorial: Creación de una API mínima con ASP.NET Core.
Información general
En este tutorial se crea la siguiente API:
API | Descripción | Cuerpo de la solicitud | Cuerpo de la respuesta |
---|---|---|---|
GET /api/todoitems |
Obtener todas las tareas pendientes | None | Matriz de tareas pendientes |
GET /api/todoitems/{id} |
Obtener un elemento por identificador | None | Tarea pendiente |
POST /api/todoitems |
Incorporación de un nuevo elemento | Tarea pendiente | Tarea pendiente |
PUT /api/todoitems/{id} |
Actualizar un elemento existente | Tarea pendiente | None |
DELETE /api/todoitems/{id} |
Eliminar un elemento | None | None |
En el diagrama siguiente, se muestra el diseño de la aplicación.
Requisitos previos
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Creación de un proyecto web
- En el menú Archivo, seleccione Nuevo>Proyecto.
- Escriba API web en el cuadro de búsqueda.
- Seleccione la plantilla API web de ASP.NET Core y seleccione Siguiente.
- En el cuadro de diálogo Configurar el nuevo proyecto, asigne al proyecto el nombre TodoApi y seleccione Siguiente.
- En el cuadro de diálogo Información adicional:
- Confirme que el Marco es .NET 8.0 (Compatibilidad a largo plazo).
- Confirme que la casilla Use controllers(uncheck to use minimal APIs) [Usar controladores (desactivar para usar API mínimas)] está activada.
- Confirme que la casilla Habilitar compatibilidad con OpenAPI está activada.
- Seleccione Crear.
Adición de un paquete NuGet
Se debe agregar un paquete NuGet para admitir la base de datos que se usa en este tutorial.
- En el menú Herramientas, seleccione Administrador de paquetes NuGet > Administrar paquetes NuGet para la solución.
- Seleccione la pestaña Examinar.
- Escriba Microsoft.EntityFrameworkCore.InMemory en el cuadro de búsqueda y, después, seleccione
Microsoft.EntityFrameworkCore.InMemory
. - Active la casilla Proyecto en el panel derecho y, después, seleccione Instalar.
Nota
Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulta los artículos de Instalación y administración de paquetes en Flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.
Prueba del proyecto
La plantilla de proyecto crea una API WeatherForecast
compatible con Swagger.
Presione Ctrl+F5 para ejecutarla sin el depurador.
Visual Studio muestra el siguiente cuadro de diálogo cuando un proyecto aún no está configurado para usar SSL:
Haga clic en Sí si confía en el certificado SSL de IIS Express.
Se muestra el cuadro de diálogo siguiente:
Si acepta confiar en el certificado de desarrollo, seleccione Sí.
Para obtener información sobre cómo confiar en el explorador Firefox, consulte Error de certificado SEC_ERROR_INADEQUATE_KEY_USAGE de Firefox.
Visual Studio inicia el explorador predeterminado y navega hasta https://localhost:<port>/swagger/index.html
, donde <port>
es un número de puerto elegido aleatoriamente en la creación del proyecto.
Se abre la página de Swagger /swagger/index.html
. Seleccione GET>Try it out>Execute (GET > Probar > Ejecutar). La página muestra lo siguiente:
- Comando de Curl para probar la API WeatherForecast
- Dirección URL para probar la API WeatherForecast
- Código de respuesta, cuerpo y encabezados
- Un cuadro de lista desplegable con los tipos de elementos multimedia y el esquema y valor de ejemplo.
Si no aparece la página Swagger, consulte este problema de GitHub.
Swagger se usa para generar una documentación útil y páginas de ayuda para API web. En este tutorial se usa Swagger para probar la aplicación. Para más información sobre Swagger, vea Documentación de la API web de ASP.NET Core con Swagger/OpenAPI.
Copie y pegue la URL de solicitud en el explorador: https://localhost:<port>/weatherforecast
.
Se devuelve un JSON similar al siguiente ejemplo:
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
Incorporación de una clase de modelo
Un modelo es un conjunto de clases que representan los datos que la aplicación administra. El modelo para esta aplicación es la clase TodoItem
.
- En el Explorador de soluciones, haga clic con el botón derecho en el proyecto. Seleccione Agregar>Nueva carpeta. Asigne a la carpeta el nombre
Models
. - Haga clic con el botón derecho en la carpeta
Models
y seleccione Agregar>Clase. Asigne a la clase el nombre TodoItem y seleccione Agregar. - Reemplace el código de plantilla por lo siguiente:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
La propiedad Id
funciona como clave única en una base de datos relacional.
Las clases de modelo pueden ir en cualquier lugar del proyecto, pero, por convención, se usa la carpeta Models
.
Incorporación de un contexto de base de datos
El contexto de base de datos es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos. Esta clase se crea derivándola de la clase Microsoft.EntityFrameworkCore.DbContext.
- Haga clic con el botón derecho en la carpeta
Models
y seleccione Agregar>Clase. Asigne a la clase el nombre TodoContext y haga clic en Agregar.
Escriba el siguiente código:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
Registro del contexto de base de datos
En ASP.NET Core, los servicios (como el contexto de la base de datos) deben registrarse con el contenedor de inserción de dependencias (DI). El contenedor proporciona el servicio a los controladores.
Actualice Program.cs
con el siguiente código resaltado:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
El código anterior:
- Agrega directivas
using
. - Agrega el contexto de base de datos para el contenedor de DI.
- Especifica que el contexto de base de datos usará una base de datos en memoria.
Scaffolding de un controlador
Haga clic con el botón derecho en la carpeta
Controllers
.Seleccione Agregar>New Scaffolded Item.
Seleccione Controlador de API con acciones mediante Entity Framework y, después, seleccione Agregar.
En el cuadro de diálogo Add API Controller with actions, using Entity Framework (Agregar controlador de API con acciones mediante Entity Framework):
- Seleccione TodoItem (TodoApi.Models) en Clase de modelo.
- Seleccione TodoContext (TodoApi.Models) en Clase de contexto de datos.
- Seleccione Agregar.
Si se produce un error en la operación de scaffolding, seleccione Agregar para probar el scaffolding una segunda vez.
El código generado:
- Marca la clase con el atributo
[ApiController]
. Este atributo indica que el controlador responde a las solicitudes de la API web. Para información sobre comportamientos específicos que permite el atributo, vea Creación de una API web con ASP.NET Core. - Utiliza la inserción de dependencias para insertar el contexto de base de datos (
TodoContext
) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.
Las plantillas de ASP.NET Core para:
- Controladores con vistas incluyen
[action]
en la plantilla de ruta. - Controladores de API no incluyen
[action]
en la plantilla de ruta.
Cuando el token [action]
no está en la plantilla de ruta, el nombre de la acción (nombre del método) no se incluye en el punto de conexión. Es decir, el nombre del método asociado a la acción no se usa en la ruta coincidente.
Actualización del método create de PostTodoItem
Actualice la instrucción "return" en PostTodoItem
para usar el operador nameof:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
El código anterior es un método HTTP POST
, indicado por el atributo [HttpPost]
. El método obtiene el valor de TodoItem
del cuerpo de la solicitud HTTP.
Para más información, vea Enrutamiento mediante atributos con atributos Http[Verb].
El método CreatedAtAction realiza las acciones siguientes:
- Devuelve un código de estado HTTP 201 cuando se ha ejecutado correctamente.
HTTP 201
es la respuesta estándar de un métodoHTTP POST
que crea un recurso en el servidor. - Agrega un encabezado Location a la respuesta. El encabezado
Location
especifica el URI de la tarea pendiente recién creada. Para obtener más información, consulte 10.2.2 201 creado. - Hace referencia a la acción
GetTodoItem
para crear el identificador URI del encabezadoLocation
. La palabra clavenameof
de C# se usa para evitar que se codifique de forma rígida el nombre de acción en la llamada aCreatedAtAction
.
Prueba de PostTodoItem
Presione Ctrl+F5 para ejecutar la aplicación.
En la ventana del explorador Swagger, seleccione POST /api/TodoItems y, a continuación, seleccione Probarlo.
En la ventana Cuerpo de la solicitud, actualiza JSON. Por ejemplo,
{ "name": "walk dog", "isComplete": true }
Seleccione Ejecutar.
Prueba del URI del encabezado de ubicación
En el POST anterior, la interfaz de usuario de Swagger muestra el encabezado de ubicación en Encabezados de respuesta. Por ejemplo, location: https://localhost:7260/api/TodoItems/1
. El encabezado de ubicación muestra el URI al recurso creado.
Para probar el encabezado de ubicación:
En la ventana del explorador Swagger, seleccione GET /api/TodoItems/{id} y, a continuación, seleccione Probarlo.
Escriba
1
en el cuadro de entradaid
y, a continuación, seleccione Ejecutar.
Examen de los métodos GET
Se implementan dos puntos de conexión GET:
GET /api/todoitems
GET /api/todoitems/{id}
En la sección anterior se muestra un ejemplo de la ruta /api/todoitems/{id}
.
Siga las instrucciones POST para agregar otro elemento de tareas pendientes y, a continuación, pruebe la ruta /api/todoitems
mediante Swagger.
Esta aplicación utiliza una base de datos en memoria. Si la aplicación se detiene e inicia, la solicitud GET anterior no devuelve ningún dato. Si no se devuelve ningún dato, publique los datos en la aplicación con POST.
Enrutamiento y rutas URL
El atributo [HttpGet]
indica un método que responde a una solicitud HTTP GET
. La ruta de dirección URL para cada método se construye como sigue:
Comience por la cadena de plantilla en el atributo
Route
del controlador:[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
Reemplace
[controller]
por el nombre del controlador, que convencionalmente es el nombre de clase de controlador sin el sufijo "Controller". En este ejemplo, el nombre de clase de controlador es TodoItemsController; por tanto, el nombre del controlador es "TodoItems". El enrutamiento en ASP.NET Core no distingue entre mayúsculas y minúsculas.Si el atributo
[HttpGet]
tiene una plantilla de ruta (por ejemplo,[HttpGet("products")]
), anéxela a la ruta de acceso. En este ejemplo no se usa una plantilla. Para más información, vea Enrutamiento mediante atributos con atributos Http[Verb].
En el siguiente método GetTodoItem
, "{id}"
es una variable de marcador de posición correspondiente al identificador único de la tarea pendiente. Al invocar a GetTodoItem
, el valor "{id}"
de la URL se proporciona al método en su parámetro id
.
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
Valores devueltos
El tipo de valor devuelto de los métodos GetTodoItems
y GetTodoItem
es ActionResult<T> type. ASP.NET Core serializa automáticamente el objeto a JSON y escribe el JSON en el cuerpo del mensaje de respuesta. El código de respuesta de este tipo de valor devuelto es 200 OK, suponiendo que no haya ninguna excepción no controlada. Las excepciones no controladas se convierten en errores 5xx.
Los tipos de valores devueltos ActionResult
pueden representar una gama amplia de códigos de estado HTTP. Por ejemplo, GetTodoItem
puede devolver dos valores de estado diferentes:
- Si no hay ningún elemento que coincida con el identificador solicitado, el método devolverá un código de error de estado 404 NotFound.
- En caso contrario, el método devuelve 200 con un cuerpo de respuesta JSON. La devolución de
item
genera una respuestaHTTP 200
.
Método PutTodoItem
Examine el método PutTodoItem
:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
es similar a PostTodoItem
, salvo por el hecho de que usa HTTP PUT
. La respuesta es 204 Sin contenido. Según la especificación HTTP, una solicitud PUT
requiere que el cliente envíe toda la entidad actualizada, no solo los cambios. Para admitir actualizaciones parciales, use HTTP PATCH.
Prueba del método PutTodoItem
En este ejemplo se usa una base de datos en memoria que se debe inicializar cada vez que se inicia la aplicación. Debe haber un elemento en la base de datos antes de que realice una llamada PUT. Llame a GET para asegurarse de que hay un elemento en la base de datos antes de realizar una llamada PUT.
Con la interfaz de usuario de Swagger, use el botón PUT para actualizar el valor TodoItem
que tiene el identificador = 1 y establezca su nombre en "feed fish"
. Observe que la respuesta es HTTP 204 No Content
.
Método DeleteTodoItem
Examine el método DeleteTodoItem
:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
Prueba del método DeleteTodoItem
Use la interfaz de usuario de Swagger para eliminar el valor TodoItem
que tiene el identificador = 1. Observe que la respuesta es HTTP 204 No Content
.
Prueba con otras herramientas
Hay muchas otras herramientas que se pueden usar para probar las API web, por ejemplo:
- Explorador de puntos de conexión de Visual Studio y archivos .http
- http-repl
- curl. Swagger usa
curl
y muestra el comandocurl
que envió. - Fiddler
Para más información, consulte:
- Tutorial de API mínima: prueba con archivos .http y el Explorador de puntos de conexión
- Instalación y prueba de API con
http-repl
Prevención del exceso de publicación
Actualmente, la aplicación de ejemplo expone todo el objeto TodoItem
. Las aplicaciones de producción suelen limitar los datos que se escriben y se devuelven mediante un subconjunto del modelo. Hay varias razones para ello y la seguridad es una de las principales. El subconjunto de un modelo se suele conocer como un objeto de transferencia de datos (DTO), modelo de entrada o modelo de vista. En este tutorial, se usa DTO.
Se puede usar un DTO para:
- Evitar el exceso de publicación.
- Ocultar las propiedades que los clientes no deben ver.
- Omitir algunas propiedades para reducir el tamaño de la carga.
- Acoplar los gráficos de objetos que contienen objetos anidados. Los gráficos de objetos acoplados pueden ser más cómodos para los clientes.
Para mostrar el enfoque del DTO, actualice la clase TodoItem
a fin de que incluya un campo secreto:
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
El campo secreto debe ocultarse en esta aplicación, pero una aplicación administrativa podría decidir exponerlo.
Compruebe que puede publicar y obtener el campo secreto.
Cree un modelo de DTO:
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Actualice el valor de TodoItemsController
para usar TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
Compruebe que no puede publicar ni obtener el campo secreto.
Llamar a la API web con JavaScript
Consulte Tutorial: Llamada a una API web de ASP.NET Core con JavaScript.
Serie de vídeos de API web
Vea Vídeo: Serie para principiantes: API web.
Patrones de aplicación web confiable
Consulte El patrón de aplicación web confiable para .NET vídeos de YouTube y artículo para obtener una guía sobre cómo crear una aplicación moderna, confiable, eficaz, comprobable, rentable y escalable ASP.NET Core, ya sea desde cero o refactorizando una aplicación existente.
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
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
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
- Federation Gateway
Importante
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, consulta Migración de ASP.NET Core 5.0 a 6.0.
Para más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Publicar en Azure
Para obtener más información sobre la implementación en Azure, consulte Inicio rápido: Implementación de una aplicación web de ASP.NET.
Recursos adicionales
Vea o descargue el código de ejemplo para este tutorial. Vea cómo descargarlo.
Para obtener más información, vea los siguientes recursos:
- Creación de API web con ASP.NET Core
- Tutorial: Creación de una API mínima con ASP.NET Core
- Documentación de la API web de ASP.NET Core con Swagger/OpenAPI
- Razor Pages con Entity Framework Core en ASP.NET Core: Tutorial 1 de 8
- Enrutar a acciones de controlador de ASP.NET Core
- Tipos de valor devuelto de acción del controlador en la API web de ASP.NET Core
- Implementar aplicaciones de ASP.NET Core en Azure App Service
- Hospedaje e implementación de ASP.NET Core
- Creación de una API web con ASP.NET Core
En este tutorial se enseñan los conceptos básicos de la creación de una API web basada en controlador que usa una base de datos. Otro enfoque para crear API en ASP.NET Core consiste en crear API mínimas. Para obtener ayuda para elegir entre las API mínimas y las API basadas en controlador, consulte Introducción a las API. Para ver un tutorial sobre cómo crear una API mínima, consulte Tutorial: Creación de una API mínima con ASP.NET Core.
Información general
En este tutorial se crea la siguiente API:
API | Descripción | Cuerpo de la solicitud | Cuerpo de la respuesta |
---|---|---|---|
GET /api/todoitems |
Obtener todas las tareas pendientes | None | Matriz de tareas pendientes |
GET /api/todoitems/{id} |
Obtener un elemento por identificador | None | Tarea pendiente |
POST /api/todoitems |
Incorporación de un nuevo elemento | Tarea pendiente | Tarea pendiente |
PUT /api/todoitems/{id} |
Actualizar un elemento existente | Tarea pendiente | None |
DELETE /api/todoitems/{id} |
Eliminar un elemento | None | None |
En el diagrama siguiente, se muestra el diseño de la aplicación.
Requisitos previos
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Creación de un proyecto web
- En el menú Archivo, seleccione Nuevo>Proyecto.
- Escriba API web en el cuadro de búsqueda.
- Seleccione la plantilla API web de ASP.NET Core y seleccione Siguiente.
- En el cuadro de diálogo Configurar el nuevo proyecto, asigne al proyecto el nombre TodoApi y seleccione Siguiente.
- En el cuadro de diálogo Información adicional:
- Confirme que el Marco es .NET 8.0 (Compatibilidad a largo plazo).
- Confirme que la casilla Use controllers(uncheck to use minimal APIs) [Usar controladores (desactivar para usar API mínimas)] está activada.
- Confirme que la casilla Habilitar compatibilidad con OpenAPI está activada.
- Seleccione Crear.
Adición de un paquete NuGet
Se debe agregar un paquete NuGet para admitir la base de datos que se usa en este tutorial.
- En el menú Herramientas, seleccione Administrador de paquetes NuGet > Administrar paquetes NuGet para la solución.
- Seleccione la pestaña Examinar.
- Escriba Microsoft.EntityFrameworkCore.InMemory en el cuadro de búsqueda y, después, seleccione
Microsoft.EntityFrameworkCore.InMemory
. - Active la casilla Proyecto en el panel derecho y, después, seleccione Instalar.
Nota:
Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulta los artículos de Instalación y administración de paquetes en Flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.
Prueba del proyecto
La plantilla de proyecto crea una API WeatherForecast
compatible con Swagger.
Presione Ctrl+F5 para ejecutarla sin el depurador.
Visual Studio muestra el siguiente cuadro de diálogo cuando un proyecto aún no está configurado para usar SSL:
Haga clic en Sí si confía en el certificado SSL de IIS Express.
Se muestra el cuadro de diálogo siguiente:
Si acepta confiar en el certificado de desarrollo, seleccione Sí.
Para obtener información sobre cómo confiar en el explorador Firefox, consulte Error de certificado SEC_ERROR_INADEQUATE_KEY_USAGE de Firefox.
Visual Studio inicia el explorador predeterminado y navega hasta https://localhost:<port>/swagger/index.html
, donde <port>
es un número de puerto elegido aleatoriamente en la creación del proyecto.
Se abre la página de Swagger /swagger/index.html
. Seleccione GET>Try it out>Execute (GET > Probar > Ejecutar). La página muestra lo siguiente:
- Comando de Curl para probar la API WeatherForecast
- Dirección URL para probar la API WeatherForecast
- Código de respuesta, cuerpo y encabezados
- Un cuadro de lista desplegable con los tipos de elementos multimedia y el esquema y valor de ejemplo.
Si no aparece la página Swagger, consulte este problema de GitHub.
Swagger se usa para generar una documentación útil y páginas de ayuda para API web. En este tutorial se usa Swagger para probar la aplicación. Para más información sobre Swagger, vea Documentación de la API web de ASP.NET Core con Swagger/OpenAPI.
Copie y pegue la URL de solicitud en el explorador: https://localhost:<port>/weatherforecast
.
Se devuelve un JSON similar al siguiente ejemplo:
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
Incorporación de una clase de modelo
Un modelo es un conjunto de clases que representan los datos que la aplicación administra. El modelo para esta aplicación es la clase TodoItem
.
- En el Explorador de soluciones, haga clic con el botón derecho en el proyecto. Seleccione Agregar>Nueva carpeta. Asigne a la carpeta el nombre
Models
. - Haga clic con el botón derecho en la carpeta
Models
y seleccione Agregar>Clase. Asigne a la clase el nombre TodoItem y seleccione Agregar. - Reemplace el código de plantilla por lo siguiente:
namespace TodoApi.Models;
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
La propiedad Id
funciona como clave única en una base de datos relacional.
Las clases de modelo pueden ir en cualquier lugar del proyecto, pero, por convención, se usa la carpeta Models
.
Incorporación de un contexto de base de datos
El contexto de base de datos es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos. Esta clase se crea derivándola de la clase Microsoft.EntityFrameworkCore.DbContext.
- Haga clic con el botón derecho en la carpeta
Models
y seleccione Agregar>Clase. Asigne a la clase el nombre TodoContext y haga clic en Agregar.
Escriba el siguiente código:
using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; public class TodoContext : DbContext { public TodoContext(DbContextOptions<TodoContext> options) : base(options) { } public DbSet<TodoItem> TodoItems { get; set; } = null!; }
Registro del contexto de base de datos
En ASP.NET Core, los servicios (como el contexto de la base de datos) deben registrarse con el contenedor de inserción de dependencias (DI). El contenedor proporciona el servicio a los controladores.
Actualice Program.cs
con el siguiente código resaltado:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
El código anterior:
- Agrega directivas
using
. - Agrega el contexto de base de datos para el contenedor de DI.
- Especifica que el contexto de base de datos usará una base de datos en memoria.
Scaffolding de un controlador
Haga clic con el botón derecho en la carpeta
Controllers
.Seleccione Agregar>New Scaffolded Item.
Seleccione Controlador de API con acciones mediante Entity Framework y, después, seleccione Agregar.
En el cuadro de diálogo Add API Controller with actions, using Entity Framework (Agregar controlador de API con acciones mediante Entity Framework):
- Seleccione TodoItem (TodoApi.Models) en Clase de modelo.
- Seleccione TodoContext (TodoApi.Models) en Clase de contexto de datos.
- Seleccione Agregar.
Si se produce un error en la operación de scaffolding, seleccione Agregar para probar el scaffolding una segunda vez.
El código generado:
- Marca la clase con el atributo
[ApiController]
. Este atributo indica que el controlador responde a las solicitudes de la API web. Para información sobre comportamientos específicos que permite el atributo, vea Creación de una API web con ASP.NET Core. - Utiliza la inserción de dependencias para insertar el contexto de base de datos (
TodoContext
) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.
Las plantillas de ASP.NET Core para:
- Controladores con vistas incluyen
[action]
en la plantilla de ruta. - Controladores de API no incluyen
[action]
en la plantilla de ruta.
Cuando el token [action]
no está en la plantilla de ruta, el nombre de la acción (nombre del método) no se incluye en el punto de conexión. Es decir, el nombre del método asociado a la acción no se usa en la ruta coincidente.
Actualización del método create de PostTodoItem
Actualice la instrucción "return" en PostTodoItem
para usar el operador nameof:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
El código anterior es un método HTTP POST
, indicado por el atributo [HttpPost]
. El método obtiene el valor de TodoItem
del cuerpo de la solicitud HTTP.
Para más información, vea Enrutamiento mediante atributos con atributos Http[Verb].
El método CreatedAtAction realiza las acciones siguientes:
- Devuelve un código de estado HTTP 201 cuando se ha ejecutado correctamente.
HTTP 201
es la respuesta estándar de un métodoHTTP POST
que crea un recurso en el servidor. - Agrega un encabezado Location a la respuesta. El encabezado
Location
especifica el URI de la tarea pendiente recién creada. Para obtener más información, consulte 10.2.2 201 creado. - Hace referencia a la acción
GetTodoItem
para crear el identificador URI del encabezadoLocation
. La palabra clavenameof
de C# se usa para evitar que se codifique de forma rígida el nombre de acción en la llamada aCreatedAtAction
.
Prueba de PostTodoItem
Presione Ctrl+F5 para ejecutar la aplicación.
En la ventana del explorador Swagger, seleccione POST /api/TodoItems y, a continuación, seleccione Probarlo.
En la ventana Cuerpo de la solicitud, actualiza JSON. Por ejemplo,
{ "name": "walk dog", "isComplete": true }
Seleccione Ejecutar.
Prueba del URI del encabezado de ubicación
En el POST anterior, la interfaz de usuario de Swagger muestra el encabezado de ubicación en Encabezados de respuesta. Por ejemplo, location: https://localhost:7260/api/TodoItems/1
. El encabezado de ubicación muestra el URI al recurso creado.
Para probar el encabezado de ubicación:
En la ventana del explorador Swagger, seleccione GET /api/TodoItems/{id} y, a continuación, seleccione Probarlo.
Escriba
1
en el cuadro de entradaid
y, a continuación, seleccione Ejecutar.
Examen de los métodos GET
Se implementan dos puntos de conexión GET:
GET /api/todoitems
GET /api/todoitems/{id}
En la sección anterior se muestra un ejemplo de la ruta /api/todoitems/{id}
.
Siga las instrucciones POST para agregar otro elemento de tareas pendientes y, a continuación, pruebe la ruta /api/todoitems
mediante Swagger.
Esta aplicación utiliza una base de datos en memoria. Si la aplicación se detiene e inicia, la solicitud GET anterior no devuelve ningún dato. Si no se devuelve ningún dato, publique los datos en la aplicación con POST.
Enrutamiento y rutas URL
El atributo [HttpGet]
indica un método que responde a una solicitud HTTP GET
. La ruta de dirección URL para cada método se construye como sigue:
Comience por la cadena de plantilla en el atributo
Route
del controlador:[Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase
Reemplace
[controller]
por el nombre del controlador, que convencionalmente es el nombre de clase de controlador sin el sufijo "Controller". En este ejemplo, el nombre de clase de controlador es TodoItemsController; por tanto, el nombre del controlador es "TodoItems". El enrutamiento en ASP.NET Core no distingue entre mayúsculas y minúsculas.Si el atributo
[HttpGet]
tiene una plantilla de ruta (por ejemplo,[HttpGet("products")]
), anéxela a la ruta de acceso. En este ejemplo no se usa una plantilla. Para más información, vea Enrutamiento mediante atributos con atributos Http[Verb].
En el siguiente método GetTodoItem
, "{id}"
es una variable de marcador de posición correspondiente al identificador único de la tarea pendiente. Al invocar a GetTodoItem
, el valor "{id}"
de la URL se proporciona al método en su parámetro id
.
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
Valores devueltos
El tipo de valor devuelto de los métodos GetTodoItems
y GetTodoItem
es ActionResult<T> type. ASP.NET Core serializa automáticamente el objeto a JSON y escribe el JSON en el cuerpo del mensaje de respuesta. El código de respuesta de este tipo de valor devuelto es 200 OK, suponiendo que no haya ninguna excepción no controlada. Las excepciones no controladas se convierten en errores 5xx.
Los tipos de valores devueltos ActionResult
pueden representar una gama amplia de códigos de estado HTTP. Por ejemplo, GetTodoItem
puede devolver dos valores de estado diferentes:
- Si no hay ningún elemento que coincida con el identificador solicitado, el método devolverá un código de error de estado 404 NotFound.
- En caso contrario, el método devuelve 200 con un cuerpo de respuesta JSON. La devolución de
item
genera una respuestaHTTP 200
.
Método PutTodoItem
Examine el método PutTodoItem
:
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
es similar a PostTodoItem
, salvo por el hecho de que usa HTTP PUT
. La respuesta es 204 Sin contenido. Según la especificación HTTP, una solicitud PUT
requiere que el cliente envíe toda la entidad actualizada, no solo los cambios. Para admitir actualizaciones parciales, use HTTP PATCH.
Prueba del método PutTodoItem
En este ejemplo se usa una base de datos en memoria que se debe inicializar cada vez que se inicia la aplicación. Debe haber un elemento en la base de datos antes de que realice una llamada PUT. Llame a GET para asegurarse de que hay un elemento en la base de datos antes de realizar una llamada PUT.
Con la interfaz de usuario de Swagger, use el botón PUT para actualizar el valor TodoItem
que tiene el identificador = 1 y establezca su nombre en "feed fish"
. Observe que la respuesta es HTTP 204 No Content
.
Método DeleteTodoItem
Examine el método DeleteTodoItem
:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
Prueba del método DeleteTodoItem
Use la interfaz de usuario de Swagger para eliminar el valor TodoItem
que tiene el identificador = 1. Observe que la respuesta es HTTP 204 No Content
.
Prueba con otras herramientas
Hay muchas otras herramientas que se pueden usar para probar las API web, por ejemplo:
- Explorador de puntos de conexión de Visual Studio y archivos .http
- http-repl
- curl. Swagger usa
curl
y muestra el comandocurl
que envió. - Fiddler
Para más información, consulte:
- Tutorial de API mínima: prueba con archivos .http y el Explorador de puntos de conexión
- Instalación y prueba de API con
http-repl
Prevención del exceso de publicación
Actualmente, la aplicación de ejemplo expone todo el objeto TodoItem
. Las aplicaciones de producción suelen limitar los datos que se escriben y se devuelven mediante un subconjunto del modelo. Hay varias razones para ello y la seguridad es una de las principales. El subconjunto de un modelo se suele conocer como un objeto de transferencia de datos (DTO), modelo de entrada o modelo de vista. En este tutorial, se usa DTO.
Se puede usar un DTO para:
- Evitar el exceso de publicación.
- Ocultar las propiedades que los clientes no deben ver.
- Omitir algunas propiedades para reducir el tamaño de la carga.
- Acoplar los gráficos de objetos que contienen objetos anidados. Los gráficos de objetos acoplados pueden ser más cómodos para los clientes.
Para mostrar el enfoque del DTO, actualice la clase TodoItem
a fin de que incluya un campo secreto:
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
El campo secreto debe ocultarse en esta aplicación, pero una aplicación administrativa podría decidir exponerlo.
Compruebe que puede publicar y obtener el campo secreto.
Cree un modelo de DTO:
namespace TodoApi.Models;
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Actualice el valor de TodoItemsController
para usar TodoItemDTO
:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
// <snippet_GetByID>
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// </snippet_GetByID>
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Update>
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
{
if (id != todoDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoDTO.Name;
todoItem.IsComplete = todoDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// </snippet_Update>
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
// <snippet_Create>
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoDTO.IsComplete,
Name = todoDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// </snippet_Create>
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
Compruebe que no puede publicar ni obtener el campo secreto.
Llamar a la API web con JavaScript
Consulte Tutorial: Llamada a una API web de ASP.NET Core con JavaScript.
Serie de vídeos de API web
Vea Vídeo: Serie para principiantes: API web.
Patrones de aplicación web confiable
Consulte El patrón de aplicación web confiable para .NET vídeos de YouTube y artículo para obtener una guía sobre cómo crear una aplicación moderna, confiable, eficaz, comprobable, rentable y escalable ASP.NET Core, ya sea desde cero o refactorizando una aplicación existente.
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
- Azure Active Directory B2C (Azure AD B2C)
- Duende Identity Server
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
- Federation Gateway
Importante
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, consulta Migración de ASP.NET Core 5.0 a 6.0.
Para más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Publicar en Azure
Para obtener más información sobre la implementación en Azure, consulte Inicio rápido: Implementación de una aplicación web de ASP.NET.
Recursos adicionales
Vea o descargue el código de ejemplo para este tutorial. Vea cómo descargarlo.
Para obtener más información, vea los siguientes recursos:
- Creación de API web con ASP.NET Core
- Tutorial: Creación de una API mínima con ASP.NET Core
- Documentación de la API web de ASP.NET Core con Swagger/OpenAPI
- Razor Pages con Entity Framework Core en ASP.NET Core: Tutorial 1 de 8
- Enrutar a acciones de controlador de ASP.NET Core
- Tipos de valor devuelto de acción del controlador en la API web de ASP.NET Core
- Implementar aplicaciones de ASP.NET Core en Azure App Service
- Hospedaje e implementación de ASP.NET Core
- Creación de una API web con ASP.NET Core