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.
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 política 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 Tim Deschryver y Rick Anderson
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 es crear API mínimas. Para obtener ayuda para elegir entre las API mínimas y las API basadas en controladores, 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:
Interfaz de Programación de Aplicaciones (API) | Descripción | Cuerpo de la solicitud | Cuerpo de la respuesta |
---|---|---|---|
GET /api/todoitems |
Obtener todas las tareas pendientes | Ninguno | Matriz de tareas pendientes |
GET /api/todoitems/{id} |
Obtener un elemento por identificador | Ninguno | 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 | Ninguno |
DELETE /api/todoitems/{id} |
Eliminar un elemento | Ninguno | Ninguno |
En el diagrama siguiente, se muestra el diseño de la aplicación.
Requisitos previos
Visual Studio 2022 con la carga de trabajo ASP.NET y desarrollo web.
Creación de un proyecto de API web
- En el menú Archivo , seleccione Nuevo>proyecto.
- Escriba API web en el cuadro de búsqueda.
- Seleccione la plantilla ASP.NET Core Web API 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 Framework es .NET 9.0 (Standard Term Support).
- Confirme que la casilla habilitar la compatibilidad con OpenAPI está activada.
- Confirme que la casilla Usar controladores (desactivar para usar las API mínimas) 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
Microsoft.EntityFrameworkCore.InMemory
seleccione . - Active la casilla Proyecto en el panel derecho y, a continuación, seleccione Instalar.
Nota:
Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en
Ejecución del proyecto
La plantilla de proyecto crea una WeatherForecast
API compatible con OpenAPI.
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:
Seleccione Sí si confía en el certificado SSL de IIS Express.
Se muestra el cuadro de diálogo siguiente:
Seleccione Sí si acepta confiar en el certificado de desarrollo.
Para obtener información sobre cómo confiar en el explorador Firefox, consulta Firefox SEC_ERROR_INADEQUATE_KEY_USAGE error de certificado.
Visual Studio inicia una ventana de terminal y muestra la dirección URL de la aplicación en ejecución. La API se hospeda en https://localhost:<port>
, donde <port>
es un número de puerto elegido aleatoriamente en la creación del proyecto.
...
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7260
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:7261
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
...
Ctrl+Haga clic en la dirección URL HTTPS de la salida para probar la aplicación web en un explorador. No hay ningún punto de conexión en https://localhost:<port>
, por lo que el explorador devuelve HTTP 404 No encontrado.
Anexe /weatherforecast
a la dirección URL para probar la API WeatherForecast.
El explorador muestra JSON similar al ejemplo siguiente:
[
{
"date": "2025-07-16",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2025-07-17",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2025-07-18",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2025-07-19",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2025-07-20",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
Prueba del proyecto
En este tutorial se usan el Explorador de puntos de conexión y los archivos .http para probar la API.
Incorporación de una clase de modelo
Un modelo es un conjunto de clases que representan los datos que administra la aplicación. 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
Models
carpeta y seleccione Agregar>clase. Asigne un nombre a la clase 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 la 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
Models
carpeta y seleccione Agregar>clase. Asigne un nombre a la clase 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 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.AddOpenApi();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
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, a continuación, seleccione Agregar.
En el cuadro de diálogo Agregar controlador de API con acciones, usando Entity Framework:
- Seleccione TodoItem (TodoApi.Models) en la clase Model.
- Seleccione TodoContext (TodoApi.Models) en la clase de contexto Data.
- Seleccione Agregar.
Si la operación de generación de plantillas falla, seleccione Agregar para intentar realizar la generación de plantillas una segunda vez.
Este paso agrega los paquetes NuGet Microsoft.VisualStudio.Web.CodeGeneration.Design
y Microsoft.EntityFrameworkCore.Tools
al proyecto.
Estos paquetes son necesarios para aplicar scaffolding.
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 obtener información sobre comportamientos específicos que habilita el atributo, consulte Creación de 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 la 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 [action]
token 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 obtener más información, consulte Enrutamiento de atributos con atributos Http[Verb].
El método CreatedAtAction realiza las acciones siguientes:
- Devuelve un código de estado HTTP 201 si se ejecuta 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
Location
encabezado especifica el URI del elemento to-do recién creado. Para obtener más información, vea 10.2.2 201 Created. - 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
Seleccione Ver>Otros Windows>Explorador de extremos.
Haga clic con el botón derecho en el punto de conexión POST y seleccione Generar solicitud.
Se crea un nuevo archivo en la carpeta del proyecto denominada
TodoApi.http
, con contenido similar al ejemplo siguiente:@TodoApi_HostAddress = https://localhost:49738 POST {{TodoApi_HostAddress}}/api/todoitems Content-Type: application/json { //TodoItem } ###
- La primera línea crea una variable que se usa para todos los puntos de conexión.
- La siguiente línea define una solicitud POST.
- Las líneas después de la línea de solicitud POST definen los encabezados y un marcador de posición para el cuerpo de la solicitud.
- La línea triple hashtag (
###
) es un delimitador de solicitud: lo que viene después es para una solicitud diferente.
La solicitud POST espera un
TodoItem
. Para definir todo, reemplace el comentario//TodoItem
por el siguiente JSON:{ "name": "walk dog", "isComplete": true }
El archivo TodoApi.http debería tener ahora un aspecto similar al del ejemplo siguiente, pero con su número de puerto:
@TodoApi_HostAddress = https://localhost:7260 Post {{TodoApi_HostAddress}}/api/todoitems Content-Type: application/json { "name": "walk dog", "isComplete": true } ###
Ejecute la aplicación.
Seleccione el vínculo Enviar solicitud situado encima de la línea de
POST
solicitud.La solicitud POST se envía a la aplicación y la respuesta se muestra en el panel Respuesta .
Prueba del URI del encabezado de ubicación
Pruebe la aplicación llamando a los GET
puntos de conexión desde un explorador o mediante el Explorador de puntos de conexión. Los pasos siguientes son para el Explorador de puntos de conexión.
En el Explorador de puntos de conexión, haga clic con el botón derecho en el primer punto de conexión GET y seleccione Generar solicitud.
El siguiente contenido se agrega al archivo
TodoApi.http
:GET {{TodoApi_HostAddress}}/api/todoitems ###
Seleccione el vínculo Enviar solicitud que está encima de la nueva
GET
lí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 respuesta es similar al siguiente JSON:
[ { "id": 1, "name": "walk dog", "isComplete": true } ]
En el Explorador de puntos de conexión, haga clic con el botón derecho en el
/api/todoitems/{id}
punto de conexión GET y seleccione Generar solicitud. El siguiente contenido se agrega al archivoTodoApi.http
:@id=0 GET {{TodoApi_HostAddress}}/api/todoitems/{{id}} ###
Asigne
{@id}
a1
(en lugar de0
).Seleccione el vínculo Enviar solicitud situado encima de la nueva línea de solicitud GET.
La solicitud GET se envía a la aplicación y la respuesta se muestra en el panel Respuesta .
El cuerpo de respuesta es similar al siguiente JSON:
{ "id": 1, "name": "walk dog", "isComplete": true }
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 /api/todoitems
ruta 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 devuelven datos, envía datos a la aplicación.
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 la clase de controlador es TodoItemsController, por lo que el nombre del controlador es "TodoItems". ASP.NET Core enrutamiento no distingue mayúsculas de 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 obtener más información, consulte Enrutamiento de 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 que devuelven los métodos GetTodoItems
y GetTodoItem
es ActionResult<T> tipo. ASP.NET Core serializa automáticamente el objeto en 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 excepciones no controladas. 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 ningún elemento coincide con el identificador solicitado, el método devuelve un código de error de estadoNotFound 404.
- 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.
Use el método PUT
para actualizar el TodoItem
que tiene Id . = 1 y establezca su nombre en "feed fish"
. Observe que la respuesta es HTTP 204 No Content
.
En el Explorador de puntos de conexión, haga clic con el botón derecho en el punto de conexión PUT y seleccione Generar solicitud.
El siguiente contenido se agrega al archivo
TodoApi.http
:PUT {{TodoApi_HostAddress}}/api/todoitems/{{id}} Content-Type: application/json { //TodoItem } ###
En la línea de solicitud PUT, reemplace
{{id}}
por1
.Reemplace el marcador de posición
//TodoItem
por las líneas siguientes:PUT {{TodoApi_HostAddress}}/api/todoitems/1 Content-Type: application/json { "id": 1, "name": "feed fish", "isComplete": false }
Seleccione el vínculo Enviar solicitud que está encima de la nueva línea de solicitud PUT.
La solicitud PUT se envía a la aplicación y la respuesta se muestra en el panel Respuesta . El cuerpo de la respuesta está vacío y el código de estado es 204.
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 el método DELETE
para eliminar el TodoItem
que tiene id . = 1. Observe que la respuesta es HTTP 204 No Content
.
En el Explorador de puntos de conexión, haga clic con el botón derecho en el punto de conexión DELETE y seleccione Generar solicitud.
Se agrega una solicitud DELETE a
TodoApi.http
.Reemplace
{{id}}
en la línea de solicitud DELETE por1
. La solicitud DELETE debe tener un aspecto similar al del ejemplo siguiente:DELETE {{TodoApi_HostAddress}}/api/todoitems/{{id}} ###
Seleccione el vínculo Enviar solicitud para la solicitud DELETE.
La solicitud DELETE se envía a la aplicación y la respuesta se muestra en el panel Respuesta . El cuerpo de la respuesta está vacío y el código de estado es 204.
Prueba con otras herramientas
Hay muchas otras herramientas que se pueden usar para probar las API web, por ejemplo:
- http-repl
-
curl. Swagger usa
curl
y muestra el comandocurl
que envió. - Violinista
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.
DTO se usa en este tutorial.
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 útil.
- Acoplar los gráficos de objetos que contienen objetos anidados. Los gráficos de objetos aplanados 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 DTO en un archivo Models/TodoItemsDTO.cs :
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 Video: Serie para principiantes sobre: APIs web.
Patrones de aplicaciones web empresariales
Para obtener instrucciones sobre cómo crear una aplicación ASP.NET Core confiable, segura, eficaz, comprobable y escalable, consulte los patrones empresariales de aplicaciones web . Hay disponible una aplicación web de ejemplo de calidad de producción completa que implementa los patrones.
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)
- Servidor Duende Identity
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
Importante
Duende Software puede requerir que pague una tarifa de licencia para el uso de producción de Duende Identity Server. Para obtener más información, consulte Migración de ASP.NET Core 5.0 a 6.0.
Para obtener más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Publicar en Azure
Para obtener 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 de este tutorial. Vea cómo descargar.
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
- Uso de los documentos openAPI generados
- documentación de api web de ASP.NET Core con Swagger/OpenAPI
- Razor Páginas con Entity Framework Core en ASP.NET Core: tutorial 1 de 8
- Enrutamiento a acciones de controlador en ASP.NET Core
- Tipos de retorno de acción del controlador en ASP.NET Core API web
- Implementación de aplicaciones ASP.NET Core en Azure App Service
- Hospedar e implementar 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 es crear API mínimas. Para obtener ayuda para elegir entre las API mínimas y las API basadas en controladores, 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:
Interfaz de Programación de Aplicaciones (API) | Descripción | Cuerpo de la solicitud | Cuerpo de la respuesta |
---|---|---|---|
GET /api/todoitems |
Obtener todas las tareas pendientes | Ninguno | Matriz de tareas pendientes |
GET /api/todoitems/{id} |
Obtener un elemento por identificador | Ninguno | 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 | Ninguno |
DELETE /api/todoitems/{id} |
Eliminar un elemento | Ninguno | Ninguno |
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 ASP.NET Core Web API 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 Framework es .NET 8.0 (Soporte a largo plazo).
- Confirme que la casilla Use controllers (desactive para usar las API mínimas) está marcada.
- Confirme que la casilla habilitar la 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
Microsoft.EntityFrameworkCore.InMemory
seleccione . - Active la casilla Proyecto en el panel derecho y, a continuación, seleccione Instalar.
Nota:
Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos bajo 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 WeatherForecast
API 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:
Seleccione Sí si confía en el certificado SSL de IIS Express.
Se muestra el cuadro de diálogo siguiente:
Seleccione Sí si acepta confiar en el certificado de desarrollo.
Para obtener información sobre cómo confiar en el explorador Firefox, consulta Firefox SEC_ERROR_INADEQUATE_KEY_USAGE error de certificado.
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>Pruébelo>Ejecutar. La página muestra lo siguiente:
- Comando 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 tipos de medios, valor de ejemplo y esquema.
Si la página Swagger no aparece, 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 obtener más información sobre Swagger, consulte ASP.NET documentación de API web core con Swagger/OpenAPI.
Copie y pegue la dirección 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 administra la aplicación. 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
Models
carpeta y seleccione Agregar>clase. Asigne un nombre a la clase 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 la 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
Models
carpeta y seleccione Agregar>clase. Asigne un nombre a la clase 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 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, a continuación, seleccione Agregar.
En el cuadro de diálogo Agregar controlador de API con acciones, utilizando Entity Framework:
- Seleccione TodoItem (TodoApi.Models) en la clase Model.
- Seleccione TodoContext (TodoApi.Models) en la clase de contexto Data.
- Seleccione Agregar.
Si se produce un error en la operación de andamiaje, seleccione Agregar para intentar el andamiaje 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 obtener información sobre comportamientos específicos que habilita el atributo, consulte Creación de 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 la 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 [action]
token 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 obtener más información, consulte Enrutamiento de atributos con atributos Http[Verb].
El método CreatedAtAction realiza las acciones siguientes:
- Devuelve un código de estado HTTP 201 si se ejecuta 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
Location
encabezado especifica el URI del elemento to-do recién creado. Para obtener más información, vea 10.2.2 201 Created. - 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 Pruébelo.
En la ventana de entrada del cuerpo de la solicitud, actualice 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 Pruébelo.
Escriba
1
en elid
cuadro de entrada y 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 otra tarea y después 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, envíe datos POST a la aplicación.
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 la clase de controlador es TodoItemsController, por lo que el nombre del controlador es "TodoItems". ASP.NET enrutamiento principal no distingue mayúsculas de 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 obtener más información, consulte Enrutamiento de 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 del tipo ActionResult<T>. ASP.NET Core serializa automáticamente el objeto en 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 excepciones no controladas. 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 ningún elemento coincide con el identificador solicitado, el método devuelve un código de error de estadoNotFound 404.
- 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, utilice el botón PUT para actualizar el elemento con Id = 1 y establecer su nombre en TodoItem
. 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
Utiliza la interfaz de usuario de Swagger para eliminar el elemento TodoItem
que tiene Id = 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ó. - Violinista
Para más información, consulte:
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.
DTO se usa en este tutorial.
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 útil.
- Acoplar los gráficos de objetos que contienen objetos anidados. Los gráficos de objetos aplanados 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 de introducción a las API web.
Patrones de aplicaciones web empresariales
Para obtener instrucciones sobre cómo crear una aplicación ASP.NET Core confiable, segura, eficaz, comprobable y escalable, consulte los patrones empresariales de aplicaciones web . Hay disponible una aplicación web de ejemplo de calidad de producción completa que implementa los patrones.
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)
- Servidor Duende Identity
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
Importante
Duende Software puede requerir que pague una tarifa de licencia para el uso de producción de Duende Identity Server. Para obtener más información, consulte Migración de ASP.NET Core 5.0 a 6.0.
Para obtener más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Publicar en Azure
Para obtener 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 de este tutorial. Vea cómo descargar.
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 api web de ASP.NET Core con Swagger/OpenAPI
- Razor Páginas con Entity Framework Core en ASP.NET Core: tutorial 1 de 8
- Enrutamiento a acciones de controlador en ASP.NET Core
- Tipos de retorno de acción del controlador en la API web de ASP.NET Core
- Implementación de aplicaciones ASP.NET Core en Azure App Service
- Hospedar e implementar 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 es crear API mínimas. Para obtener ayuda para elegir entre las API mínimas y las API basadas en controladores, 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:
Interfaz de Programación de Aplicaciones (API) | Descripción | Cuerpo de la solicitud | Cuerpo de la respuesta |
---|---|---|---|
GET /api/todoitems |
Obtener todas las tareas pendientes | Ninguno | Matriz de tareas pendientes |
GET /api/todoitems/{id} |
Obtener un elemento por identificador | Ninguno | 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 | Ninguno |
DELETE /api/todoitems/{id} |
Eliminar un elemento | Ninguno | Ninguno |
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 ASP.NET Core Web API 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 Framework es .NET 8.0 (Soporte Técnico a Largo Plazo).
- Confirme que la casilla para Usar controladores (desmarque para usar APIs mínimas) está marcada.
- Confirme que la casilla habilitar la 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
Microsoft.EntityFrameworkCore.InMemory
seleccione . - Active la casilla Proyecto en el panel derecho y, a continuación, seleccione Instalar.
Nota:
Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos de
Prueba del proyecto
La plantilla de proyecto crea una WeatherForecast
API 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:
Seleccione Sí si confía en el certificado SSL de IIS Express.
Se muestra el cuadro de diálogo siguiente:
Seleccione Sí si acepta confiar en el certificado de desarrollo.
Para obtener información sobre cómo confiar en el explorador Firefox, consulta Firefox SEC_ERROR_INADEQUATE_KEY_USAGE error de certificado.
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>Pruébelo>Ejecutar. La página muestra lo siguiente:
- Comando 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 tipos de medios, valor de ejemplo y esquema.
Si la página de Swagger no aparece, consulte esta incidencia 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 obtener más información sobre Swagger, consulte ASP.NET documentación de API web core con Swagger/OpenAPI.
Copie y pegue la dirección 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 administra la aplicación. 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
Models
carpeta y seleccione Agregar>clase. Asigne un nombre a la clase 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 la 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
Models
carpeta y seleccione Agregar>clase. Asigne un nombre a la clase 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 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, a continuación, seleccione Agregar.
En el cuadro de diálogo Agregar controlador de API con acciones, usando Entity Framework:
- Seleccione TodoItem (TodoApi.Models) en la clase Model.
- Seleccione TodoContext (TodoApi.Models) en la clase de contexto Data.
- Seleccione Agregar.
Si se produce un error en la operación de andamiaje, seleccione Agregar para intentarlo 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 obtener información sobre comportamientos específicos que habilita el atributo, consulte Creación de 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 la 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 [action]
token 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 obtener más información, consulte Enrutamiento de atributos con atributos Http[Verb].
El método CreatedAtAction realiza las acciones siguientes:
- Devuelve un código de estado HTTP 201 si se ejecuta 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
Location
encabezado especifica el URI del elemento to-do recién creado. Para obtener más información, vea 10.2.2 201 Created. - 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 Pruébelo.
En la ventana Entrada del cuerpo de la solicitud, actualice el 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 Pruébelo.
Escriba
1
en elid
cuadro de entrada y 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 otra tarea pendiente y, a continuación, pruebe la ruta /api/todoitems
usando 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, envíe POST datos a la aplicación.
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 la clase de controlador es TodoItemsController, por lo que el nombre del controlador es "TodoItems". ASP.NET enrutamiento principal no distingue mayúsculas de 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 obtener más información, consulte Enrutamiento de 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 del tipo ActionResult<T>. ASP.NET Core serializa automáticamente el objeto en 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 excepciones no controladas. 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 ningún elemento coincide con el identificador solicitado, el método devuelve un código de error de estadoNotFound 404.
- 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, utilice el botón PUT para actualizar el elemento con Id = 1 y establecer su nombre en TodoItem
. 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
Utiliza la interfaz de usuario de Swagger para eliminar el elemento TodoItem
que tiene Id = 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ó. - Violinista
Para más información, consulte:
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.
DTO se usa en este tutorial.
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 útil.
- Acoplar los gráficos de objetos que contienen objetos anidados. Los gráficos de objetos aplanados 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 sobre API web.
Patrones de aplicaciones web empresariales
Para obtener instrucciones sobre cómo crear una aplicación ASP.NET Core confiable, segura, eficaz, comprobable y escalable, consulte los patrones empresariales de aplicaciones web . Hay disponible una aplicación web de ejemplo de calidad de producción completa que implementa los patrones.
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)
- Servidor Duende Identity
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
Importante
Duende Software puede requerir que pague una tarifa de licencia para el uso de producción de Duende Identity Server. Para obtener más información, consulte Migración de ASP.NET Core 5.0 a 6.0.
Para obtener más información, consulte la documentación de Duende Identity Server (sitio web de Duende Software).
Publicar en Azure
Para obtener 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 de este tutorial. Vea cómo descargar.
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 api web de ASP.NET Core con Swagger/OpenAPI
- Razor Páginas con Entity Framework Core en ASP.NET Core: tutorial 1 de 8
- Enrutamiento a acciones de controlador en ASP.NET Core
- Tipos de retorno de acciones del controlador en API web de ASP.NET Core
- Implementación de aplicaciones ASP.NET Core en Azure App Service
- Hospedar e implementar ASP.NET Core
- Creación de una API web con ASP.NET Core