Agregar un Endpoint POST de Inserción con Validación en .NET Aspire en Linux
[[academy-video-youtube({"vid": "oAMMHR8kKnw", "start_time": "0", "title": "Agregar un Punto Final de Inserción POST con Validación en .NET Aspire en Linux", "creator": "Tim Corey", "length": "20m 31s"})]]
Leer datos de una API es solo la mitad de la historia. Eventualmente, cada aplicación necesita aceptar nuevos registros, y eso significa construir un punto final POST que reciba un cuerpo de solicitud, valide la entrada, la persista en la base de datos y devuelva un código de estado significativo. Saltar el paso de validación es tentador durante la creación de prototipos, pero las API de producción que aceptan entradas no validadas se convierten en una fuente de datos corruptos que es más difícil de limpiar que de prevenir.
En su video "Agregar un Punto Final de Inserción POST con Validación en .NET Aspire en Linux", Tim Corey agrega un punto final de inserción a la API de Tiny Ticket, crea un tipo de registro de entrada dedicado, conecta la canalización de validación integrada de .NET para APIs mínimas, y configura Swagger para lanzarse automáticamente cuando la API se inicia. El episodio cubre el ciclo completo desde el procedimiento almacenado hasta el punto final probado, incluyendo el formato de respuesta de error de validación que .NET devuelve de forma predeterminada. Si estás siguiendo la serie C# en Linux o agregando operaciones de escritura a una API mínima por primera vez, este artículo guía paso a paso.
Creando el Tipo de Registro de Inserción
[1:46 - 4:43] Antes de construir el punto final, Tim crea un objeto de transferencia de datos que representa la forma de una solicitud de inserción. El existente TicketModel incluye campos como Id y CreatedDate que la base de datos genera automáticamente. Aceptar esos en un cuerpo POST sería ignorar o causar conflictos, por lo que un tipo separado delimita la entrada solo a los campos que el solicitante debe proporcionar.
public record TicketInsertRecord(string Title, string Description, int Priority);public record TicketInsertRecord(string Title, string Description, int Priority);Usar un record en lugar de un class es una elección deliberada. Los registros brindan igualdad basada en valores e inmutabilidad por defecto, lo cual se ajusta a la semántica de una carga de solicitud: los datos llegan, se validan, se pasan a la base de datos y nunca se modifican de por medio. Las tres propiedades (Título, Descripción, Prioridad) se asignan directamente a los parámetros del procedimiento almacenado spTickets_Insert.
Mapeo del Endpoint POST
[4:43 - 9:51] Con el tipo de registro definido, el registro del punto final sigue el mismo patrón que las rutas GET pero utiliza MapPost y vincula el cuerpo de la solicitud al registro de inserción:
app.MapPost("/api/tickets", async (TicketInsertRecord ticket, IDbConnection db) =>
{
await db.SaveDataAsync("spTickets_Insert", ticket);
return Results.NoContent();
});app.MapPost("/api/tickets", async (TicketInsertRecord ticket, IDbConnection db) =>
{
await db.SaveDataAsync("spTickets_Insert", ticket);
return Results.NoContent();
});Nótese que la ruta es /api/tickets sin un segmento de ID, coincidiendo con las convenciones de REST donde POST a una URL de colección crea un nuevo recurso. El controlador llama al procedimiento almacenado con el objeto completo ticket como la bolsa de parámetros. Dapper asigna las propiedades del registro a los parámetros SQL por nombre.
Devolver Results.NoContent() envía un código de estado 204. Tim explica el razonamiento: la inserción tuvo éxito, pero no hay nada significativo que devolver en el cuerpo de la respuesta. Algunas APIs devuelven el objeto recién creado con un estado 201 Created y un encabezado Location que apunta al nuevo recurso, lo cual es una alternativa válida. Para el proyecto Tiny Ticket, el 204 mantiene las cosas simples.
Prueba de Insertar a través de Swagger
[9:51 - 14:43] Tim launches the project and navigates to Swagger. El punto final POST aparece con un esquema de cuerpo de solicitud que coincide con las propiedades TicketInsertRecord. Llena un ticket de prueba con un título, descripción y prioridad, y luego ejecuta la solicitud.
Un 204 regresa, confirmando que la inserción tuvo éxito. Para verificar que los datos realmente persistieron, cambia al endpoint GET all y lo ejecuta. El nuevo ticket aparece en la lista junto a los registros de prueba originales.
Lo que la prueba también revela es la brecha sin validación: enviar un título vacío, una descripción faltante o una prioridad de 99, todo tiene éxito con un 204. La base de datos acepta lo que la API envía. Esa brecha motiva la siguiente sección.
Agregando Validación Integrada
[14:43 - 18:28] A partir de .NET 10, las API mínimas soportan un tubería de validación integrada que lee atributos de anotación de datos del tipo de entrada y rechaza solicitudes inválidas antes de que el manejador se ejecute. Tim lo conecta en dos pasos.
Primero, registre los servicios de validación en Program.cs. Esta sola línea activa toda la tubería:
builder.Services.AddValidation();builder.Services.AddValidation();Con el servicio registrado, el marco inspecciona cada cuerpo de solicitud en búsqueda de atributos de validación antes de que el manejador corra. El segundo paso es anotar el registro de inserción con las reglas que cada campo debe satisfacer:
public record TicketInsertRecord(
[Required, MinLength(1)] string Title,
[Required] string Description,
[Range(1, 5)] int Priority
);public record TicketInsertRecord(
[Required, MinLength(1)] string Title,
[Required] string Description,
[Range(1, 5)] int Priority
);[Required] asegura que el campo esté presente y no sea nulo. [MinLength(1)] evita que las cadenas vacías pasen la verificación requerida (ya que una cadena vacía técnicamente no es nula). [Range(1, 5)] restringe la prioridad a un nivel válido. Estos atributos son los mismos tipos System.ComponentModel.DataAnnotations que los controladores ASP.NET MVC han usado durante años, pero ahora funcionan en APIs mínimas sin middleware adicional.
Después de guardar y reiniciar, Tim envía una solicitud con un título vacío y una prioridad de 10. La respuesta regresa como un 400 Bad Request con un cuerpo de error estructurado:
{
"errors": {
"Title": ["The Title field is required."],
"Priority": ["The field Priority must be between 1 and 5."]
}
}La tubería de validación corta el circuito de la solicitud antes de que el manejador se ejecute, así que no llegan datos inválidos a la base de datos. La respuesta de error sigue el formato RFC 7807 Problem Details, que los consumidores de API pueden analizar programáticamente.
Auto-Lanzamiento de Swagger al Inicio
[19:44 - 20:31] Una pequeña mejora en la calidad de vida cierra el episodio. Cada vez que Tim lanzaba la API, tenía que escribir manualmente /swagger en la URL del navegador. Para automatizar eso, abre el Properties/launchSettings.json del proyecto de API y agrega una propiedad launchUrl al perfil HTTPS:
{
"profiles": {
"https": {
"launchUrl": "swagger"
}
}
}En el próximo lanzamiento, el navegador abre directamente la Swagger UI en lugar de la página predeterminada. Esto ahorra unos segundos por ciclo de depuración, lo cual se acumula a lo largo de una sesión de desarrollo completa.
Conclusión
[20:09 - 20:31] Agregar un punto final POST a una API mínima implica crear un registro de entrada dedicado, mapearlo a MapPost con la URL de colección, y llamar al procedimiento almacenado con el registro como el objeto de parámetro. La validación en .NET 10 requiere un registro de servicio y atributos de anotación de datos estándar en las propiedades de registro. El marco maneja el formato de respuesta 400 automáticamente.
Navegación de la serie: Este artículo es parte de la serie C# en Linux construyendo la aplicación Tiny Ticket. Anterior: Agregar un Punto de Acceso Get por ID. Siguiente: Agregar un Punto de Acceso PUT Update.
Consejo de ejemplo: cuando su procedimiento almacenado de inserción devuelve el ID del nuevo registro, cambie el retorno de Results.NoContent() a Results.Created($"/api/tickets/{newId}", result) para dar a los llamadores un estado 201 con un encabezado de ubicación que puedan seguir para obtener el recurso creado.
Vea el video completo en su canal de YouTube y obtenga más información sobre cómo construir endpoints de escritura en la serie C# en Linux.

