Digamos que estoy modelando una API REST de blogs que tiene recursos Blog , Post y Comment . Luego agrego los siguientes URI para los recursos de Blog y Post :
/api/blogs /api/blogs/{blogId} /api/blogs/{blogId}/posts y dado que se debe evitar el anidamiento profundo, creo un punto final separado para todas las Post para obtener sus comentarios:
/api/posts /api/posts/{postId} /api/posts/{postId}/comments Ahora, dado que se puede acceder al recurso Post desde dos URI diferentes como este:
¿Cómo debo implementar esto en el proyecto ASP.NET Core API sin duplicación de código innecesaria?
P.ej. ¿Debería implementar estos dos puntos finales en la misma acción del mismo controlador, o debería separar esto en dos controladores (por ejemplo, manejar /api/posts?blogId=123 en PostsController y /api/blogs/123/posts en BlogsController ) ? Además, ¿debería implementar acciones POST , PUT y DELETE en ambos puntos finales o simplemente elegir uno como URI principal?
Después de entender cómo hacer esto, ¿está bien generalizar el mismo enfoque para otros recursos con el mismo tipo de relación (por ejemplo, Post y Comment )?
Debe hacer controladores separados para cada recurso. Lo que esos dos controladores pueden compartir es potencialmente un PostService u otro mecanismo para obtener publicaciones. También tenga en cuenta que si está utilizando Entity Framework o algún otro ORM, las Publicaciones pueden estar expuestas en el objeto Blog a través de una relación. Por lo tanto, su acción podría ser tan simple como:
public async Task<IActionResult> GetPostsForBlog(int blogId) { return Ok(_context.Blogs.Find(blogId).Posts); }Elegiría uno de los dos caminos y me quedaría con él.
Por ejemplo, si va a pasar parámetros en la ruta:
BlogsController
PostsController
ComentariosControlador
Hay una diferencia entre dos enfoques y se trata de problemas de rendimiento en el lado de la base de datos.
Cuando use /api/blogs/123/posts , debe incluir Posts en el objeto Blog como este:
_dbContext.Blogs .AsNoTracking() .Include(x => x.Posts) .Where(x => x.Id == 123) .FirstOrDefaultAsync(); Y esto se traducirá a T-Sql y usará Inner Join o Left Join (según las relaciones requeridas) entre la tabla del blog y la tabla de publicación, algo como esto:
Select * from (Select top 1 * from dbo.Blogs where Id = 123) b Left Join dbo.Posts p on b.Id = p.BlogId Pero cuando llama a /api/posts?blogId=123 , simplemente puede consultar el objeto Post de esta manera:
_dbContext.Posts .AsNoTracking() .Where(x => x.BlogId == 123) .ToListAsync(); Y esto se traducirá a T-Sql así:
Select * from dbo.Posts where BlogId=123Como puede adivinar, la segunda consulta es más rápida que la primera.
Si desea seguir el estándar Rest Api , debe seguir el primero, pero para obtener un mejor rendimiento, use la segunda consulta para obtener elementos de Posts y no es necesario implementar dos enfoques, solo elija uno porque con implementar dos enfoques ya implementa la lógica en dos lugares y si la lógica cambió, debe cambiar dos lugares de su código.