Saltar al pie de página
Iron Academy Logo
Aprender C#
Aprender C#

Otras categorías

Agregar un Endpoint DELETE en .NET Aspire en Linux

[[academy-video-youtube({"vid": "x10CYBXrLxg", "start_time": "0", "title": "Adding a DELETE Endpoint in .NET Aspire on Linux", "creator": "Tim Corey", "length": "8m 40s"})]]

Toda API CRUD eventualmente necesita una forma de eliminar registros, y el verbo DELETE cierra las cuatro operaciones HTTP principales junto con GET, POST, y PUT. Comparado con los otros verbos, DELETE es estructuralmente el más simple: sin cuerpo de solicitud, sin pipeline de validación, sin tipo de retorno complejo. Lo que introduce es una pregunta de diseño que no tiene una respuesta universalmente correcta, a saber, qué devolver cuando el llamador solicita eliminar un registro que no existe.

En su video "Adding a DELETE Endpoint in .NET Aspire on Linux", Tim Corey concluye la API de Tiny Ticket añadiendo el punto final final, corrige una inconsistencia de mayúsculas en un parámetro que había estado silenciosamente en el procedimiento almacenado GET-by-ID, y discute cuándo devolver un 404 frente a un 204 para un registro faltante. El episodio también anticipa el cambio al front end, que se convertirá en el enfoque de la siguiente fase de la serie de C# en Linux. Si has estado siguiendo la serie o estás conectando DELETE en una API mínima por primera vez, este artículo recorre el punto final completo y el pequeño refactor que hizo el enlace del parámetro coherente en todo el proyecto.

Mapeo del Punto Final DELETE

[1:02 - 2:14] El registro del punto final sigue la misma forma que las otras rutas, con dos ajustes. La ruta incluye un segmento {id:int} para que el ID se pase en la URL en lugar de en el cuerpo, y la firma del controlador usa MapDelete en lugar de MapPost o MapPut. No hay registro de entrada porque no se necesita nada más allá del identificador.

app.MapDelete("/api/tickets/{id:int}",
    async Task<Results<NoContent, ValidationProblem>>
    (ISqlDataAccess sql, int id) =>
{
    await sql.SaveDataAsync("dbo.spTickets_Delete",
        new { Id = id }, "TicketDB");
    return TypedResults.NoContent();
});
app.MapDelete("/api/tickets/{id:int}",
    async Task<Results<NoContent, ValidationProblem>>
    (ISqlDataAccess sql, int id) =>
{
    await sql.SaveDataAsync("dbo.spTickets_Delete",
        new { Id = id }, "TicketDB");
    return TypedResults.NoContent();
});

El controlador llama al procedimiento almacenado spTickets_Delete a través del contenedor Dapper, pasando un objeto anónimo con el ID. Devolver TypedResults.NoContent() produce un estado 204, señalando que la operación tuvo éxito y no hay cuerpo de respuesta para devolver. La declaración del tipo de retorno refleja el punto final PUT del episodio anterior, ya que ambas operaciones tienen el mismo conjunto de posibles resultados desde la perspectiva del marco.

Corrigiendo la Discrepancia en el Uso de Mayúsculas en el Parámetro

[2:14 - 4:32] Mientras configura la llamada DELETE, Tim nota una inconsistencia que introdujo anteriormente en la serie. El procedimiento almacenado spTickets_Delete usa un parámetro Id en mayúsculas, lo que significa que el objeto anónimo necesita una asignación explícita de Id = id. El procedimiento spTickets_Update también usa Id en mayúsculas. Pero spTickets_Get, el procedimiento detrás del punto de acceso GET-por-ID, usa id en minúsculas. Esa variante en minúsculas permitió al controlador original pasar new { id } sin la asignación explícita, lo cual parecía conveniente en ese momento, pero dejó la base de código inconsistente.

En lugar de mantener la asimetría, abre SQL Server Management Studio y altera el procedimiento GET para usar Id en mayúsculas:

ALTER PROCEDURE spTickets_Get
    @Id int
AS
BEGIN
    SELECT Id, Title, Description, DateCompleted, Priority, CreatedDate
    FROM dbo.Tickets
    WHERE Id = @Id;
END

Con el procedimiento actualizado, el controlador GET en Program.cs ahora necesita la misma asignación explícita que usan los controladores DELETE y PUT, cambiando de new { id } a new { Id = id }. El cambio es mecánico, pero la razón importa: la consistencia en el uso de mayúsculas en los parámetros en todos los procedimientos almacenados significa que cada punto final enlaza los parámetros de la misma manera, lo que elimina una pequeña pero real fuente de confusión al leer la capa de acceso a datos más tarde. Una convención que solo se mantiene en uno de cuatro lugares no es una convención.

Cuándo Devolver 204 vs. 404 para un Registro Faltante

[4:46 - 5:46] Después de que el punto final compila, Tim hace una pausa sobre una pregunta de diseño que surge con cada implementación DELETE. Si el llamador pasa un ID que no existe, ¿qué debería devolver la API? Existen dos respuestas razonables.

Devolver 204 NoContent independientemente de si se eliminó una fila trata la solicitud como idempotente. Desde la perspectiva del llamador, el recurso ha desaparecido, que era el objetivo. Esto es lo que hace actualmente el manejador, y es con lo que el proyecto Tiny Ticket se enviará. Devolver 404 NotFound para un registro faltante le da al llamador más información, pero requiere que el procedimiento almacenado informe si una fila fue realmente eliminada, típicamente devolviendo un recuento de filas que el manejador puede inspeccionar antes de decidir qué respuesta enviar.

Para una API CRUD interna donde el front end ya sabe qué IDs existen (porque acaba de cargar la lista), 204 está bien. Para una API pública donde los llamadores podrían adivinar IDs, 404 previene la ilusión silenciosa de que los datos han sido eliminados cuando nunca existieron. Tim señala que devolver 404 puede filtrar información sobre qué IDs existen en la base de datos, aunque para una operación de eliminación el riesgo práctico es bajo ya que ejercitar el punto final ya implica acceso de escritura.

Probando A Través de Swagger

[5:46 - 7:08] With the database running, Tim launches the API and opens Swagger. Comienza con GET all para tomar nota de los datos actuales: registros 1, 2, 3 del origen original, además de 107, 109, y 110 sobrantes de pruebas de inserción anteriores.

Ejecuta DELETE en 107 y recibe de vuelta un 204. Lo mismo para 110. Para verificar el comportamiento de registro faltante, ejecuta DELETE en 1011, un ID que nunca estuvo en la base de datos. La respuesta sigue siendo 204, sin indicación de que no se eliminó nada. Ese es el compromiso discutido en la sección previa, ahora visible en la respuesta real de la API.

Un segundo GET all confirma el estado final: los registros 1, 2, 3, y 109 permanecen. El punto final DELETE funciona para IDs válidos y falla en silencio para los no válidos, exactamente como lo especifica la implementación.

Conclusión: CRUD Completo

[7:08 - 8:38] Añadir DELETE completa los cuatro verbos CRUD para la API de Tiny Ticket. El mismo patrón estructural se lleva a través de cada punto final: definición de ruta, nombre de procedimiento almacenado, llamada de acceso a datos Dapper, resultado tipado. Tim es franco al mencionar que una API de producción probablemente añadiría más puntos finales, como un PATCH para marcar un ticket como completado sin enviar todo el objeto a través del cable, o un punto final de búsqueda dedicado. El objetivo de la serie, sin embargo, es mantener cada capa enfocada para que la siguiente capa (el front end) tenga una superficie limpia para llamar.

La consistencia es lo que hace que el proyecto sea cómodo de leer. El envoltorio Dapper, el patrón de inserción POST, la pipeline de validación, y los resultados tipados se combinan para que cada nuevo punto final tome aproximadamente la misma cantidad de código independientemente de qué verbo implemente. Esa predictibilidad es lo que hace que la API sea un objetivo agradable para el trabajo de front end que sigue.

Conclusión

[8:38 - 8:40] Agregar un punto de acceso DELETE a una API mínima requiere un registro MapDelete con un segmento de ID en la ruta, una llamada al procedimiento almacenado a través del contenedor de acceso a datos, y un retorno TypedResults.NoContent(). El punto final completa la superficie CRUD para el proyecto Tiny Ticket y prepara el paso al front end en la siguiente fase de la serie.

Navegación de la serie: Este artículo es parte de la serie de C# en Linux construyendo la app Tiny Ticket. Anterior: Añadiendo un Punto Final de Actualización PUT. Próxima fase: páginas front end que consumen la API.

Consejo de ejemplo: Si desea una respuesta DELETE más informativa sin cambiar el procedimiento almacenado, capture el recuento de filas afectadas de SaveDataAsync y devuelva un TypedResults.NotFound() cuando el conteo sea cero. Esto añade la ruta 404 sin reestructurar la capa de acceso a datos.

Mira el video completo en su canal de YouTube y obtén más información sobre la construcción de puntos finales CRUD en la serie de C# en Linux.

Hero Worlddot related to Agregar un Endpoint DELETE en .NET Aspire en Linux
Hero Affiliate related to Agregar un Endpoint DELETE en .NET Aspire en Linux

Gana más compartiendo lo que te gusta

¿Creas contenidos para desarrolladores que trabajan con .NET, C#, Java, Python o Node.js? ¡Convierte tu experiencia en un ingreso extra!

Equipo de soporte de Iron

Estamos disponibles online las 24 horas, 5 días a la semana.
Chat
Email
Llámame