Agregar un Endpoint PUT de Actualización en .NET Aspire en Linux
[[academy-video-youtube({"vid": "hSRI_JKiH5M", "start_time": "0", "title": "Adding a PUT Update Endpoint in .NET Aspire on Linux", "creator": "Tim Corey", "length": "8m 43s"})]]
Una vez que una API puede leer y crear registros, la siguiente operación es actualizar los existentes. Un punto de acceso PUT reemplaza todo el recurso con los datos que el llamador proporciona, lo que significa que el cuerpo de la solicitud necesita cada campo, no solo los que cambiaron. Esa distinción entre PUT (reemplazo completo) y PATCH (modificación parcial) es importante para cómo diseñar el tipo de entrada y cómo los llamadores interactúan con el punto de acceso.
En su video "Agregando un Endpoint de Actualización PUT en .NET Aspire en Linux", Tim Corey agrega el endpoint de actualización al API de Tiny Ticket, crea un tipo de registro de actualización dedicado que incluye campos que el registro de inserción no tenía (como ID y fecha completada), aplica atributos de validación, y prueba el ciclo completo a través de Swagger. El episodio sigue el mismo patrón establecido en entregas anteriores, pero introduce un campo anulable DateTime y la diferencia entre las semánticas de PUT y PATCH. Si estás creando endpoints CRUD en una API mínima, este artículo cubre el lado de la actualización.
Creando el Tipo de Registro de Actualización
[1:54 - 4:04] El registro de inserción del episodio anterior aceptaba Título, Descripción y Prioridad. El registro de actualización necesita dos campos adicionales: el ID del ticket que se está modificando y la marca de tiempo DateCompleted. Tim copia el registro de inserción y lo ajusta.
public record TicketUpdateRecord(
[Required, Range(1, int.MaxValue)] int Id,
[Required, MinLength(1)] string Title,
[Required] string Description,
DateTime? DateCompleted,
[Range(1, 5)] int Priority
);public record TicketUpdateRecord(
[Required, Range(1, int.MaxValue)] int Id,
[Required, MinLength(1)] string Title,
[Required] string Description,
DateTime? DateCompleted,
[Range(1, 5)] int Priority
);Marcar Id como [Required] con una restricción [Range(1, int.MaxValue)] previene que valores negativos o cero lleguen a la base de datos. DateCompleted es un DateTime? anulable porque un ticket que aún no se ha resuelto no debería requerir una fecha de finalización. No se necesita un atributo de validación en él, ya que null es un estado válido.
Para asegurar que las propiedades del registro coincidan exactamente, Tim extrae la lista de campos del procedimiento almacenado spTickets_Update. Esa alineación permite a Dapper mapear el registro directamente sin ningún cableado manual de propiedad a parámetro.
Mapeo del Endpoint PUT
[4:04 - 5:44] El registro del endpoint sigue el patrón establecido. MapPut se vincula a la ruta /api/tickets, y el manejador llama al procedimiento almacenado con el registro de actualización:
app.MapPut("/api/tickets", async Task<Results<NoContent, ValidationProblem>>
(TicketUpdateRecord ticket, ISqlDataAccess sql) =>
{
await sql.SaveDataAsync("dbo.spTickets_Update", ticket, "TicketDB");
return TypedResults.NoContent();
});app.MapPut("/api/tickets", async Task<Results<NoContent, ValidationProblem>>
(TicketUpdateRecord ticket, ISqlDataAccess sql) =>
{
await sql.SaveDataAsync("dbo.spTickets_Update", ticket, "TicketDB");
return TypedResults.NoContent();
});Declarar Results<NoContent, ValidationProblem> como el tipo de retorno le dice al marco que el endpoint produce un 204 en caso de éxito o un 400 si la validación falla. La variante ValidationProblem se maneja automáticamente por el pipeline registrado en el episodio anterior; el mismo manejador solo necesita devolver el caso de éxito.
Vale la pena notar cómo el envoltorio Dapper mantiene el acceso a datos conciso: nombre del procedimiento almacenado, modelo, nombre de la cadena de conexión. Tres parámetros cubren toda la llamada a la base de datos. El envoltorio fue escrito anteriormente en la serie y continúa dando frutos ya que cada nuevo endpoint lo reutiliza sin modificaciones.
PUT vs. PATCH: Cuando el Reemplazo Completo Importa
[6:06 - 6:46] Antes de probar, Tim hace una pausa para aclarar la diferencia entre PUT y PATCH. Una solicitud PUT reemplaza todo el recurso: cada campo en el cuerpo de la solicitud sobrescribe la columna correspondiente de la base de datos, incluso si el llamador no tenía la intención de cambiarlo. Una solicitud PATCH solo actualiza los campos incluidos en el cuerpo.
Para el proyecto Tiny Ticket, PUT es la elección correcta porque el front end cargará el ticket completo, permitirá que el usuario edite los campos y enviará el objeto completo de vuelta. En una aplicación en producción, Tim menciona que probablemente agregaría un endpoint PATCH específicamente para operaciones comunes de un solo campo como marcar un ticket como completado, donde enviar todo el objeto solo para cambiar una fecha se siente innecesario.
Prueba de la Actualización a través de Swagger
[6:46 - 8:26] Tim launches the API and opens Swagger. Antes de probar el PUT, ejecuta el endpoint GET all para verificar el estado actual de los datos. Uno de los registros de prueba (ID 109) tiene valores vacíos para el título, la descripción y la prioridad de pruebas anteriores. Eso se convierte en el objetivo para la actualización.
Llena el cuerpo de la solicitud PUT con el ID 109, un título de "Registro de Ejemplo", una descripción y una prioridad de 5. Después de ejecutar, la respuesta regresa como 204. Ejecutar GET all nuevamente confirma que el registro ahora tiene los valores actualizados.
Para verificar la validación, borra el campo de título y vuelve a ejecutar. La respuesta devuelve un 400 con un mensaje de error estructurado: "El campo de título del ticket es obligatorio." Los mismos atributos de validación del endpoint de inserción se trasladan al registro de actualización porque utilizan el mismo patrón de anotación.
Conclusión: Progreso CRUD
[8:26 - 8:43] Con el endpoint PUT completo, la API de Tiny Ticket ahora cubre tres de las cuatro operaciones CRUD: leer (GET all y GET por ID), crear (POST) y actualizar (PUT). Cada endpoint sigue el mismo patrón estructural, lo que hace predecible el código base. La operación restante es DELETE, que Tim adelanta como el próximo episodio.
Conclusión
[8:38 - 8:43] Añadir un endpoint PUT a una API mínima requiere un registro de actualización dedicado con atributos de validación, un registro MapPut con la URL de la colección y una llamada al procedimiento almacenado a través del envoltorio de acceso a datos. El tipo de retorno Results<NoContent, ValidationProblem> permite que el marco maneje tanto las respuestas de éxito como las de fallos de validación. Campos anulables como DateTime? pasan sin requerir un atributo de validación ya que null es un valor válido para datos incompletos.
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 Endpoint de Inserción POST. Siguiente: Agregar un Endpoint DELETE.
Consejo de Ejemplo: Si su procedimiento almacenado de actualización devuelve el recuento de filas modificadas, verifíquelo antes de devolver 204. Un recuento de cero significa que el ID no coincidió con ningún registro, y debería devolver un 404 en lugar de tener éxito silenciosamente.
Vea el video completo en su canal de YouTube y obtenga más información sobre cómo construir endpoints CRUD en la serie C# en Linux.

