Let's say I am modeling blogging REST API which has resources Blog
, Post
and Comment
. Then I add following URIs for Blog
and Post
resources:
/api/blogs
/api/blogs/{blogId}
/api/blogs/{blogId}/posts
and since deep nesting should be avoided I create separate endpoint for all Post
s in order to get their Comment`s:
/api/posts
/api/posts/{postId}
/api/posts/{postId}/comments
Now, since Post
resource can be accessed from two different URIs like this:
how should I implement this in ASP.NET Core API project without unnecessary code duplication?
Eg. should I implement both of these endpoints in the same action of the same controller, or should I separate this in two controllers (eg. handle /api/posts?blogId=123
in PostsController
and /api/blogs/123/posts
in BlogsController
)? Also, should I implement POST
, PUT
and DELETE
actions on both endpoints or just choose one as the primary URI?
After I understand how to do this, is it ok to generalize the same approach for other resources with the same kind of relationship (eg. Post
and Comment
)?
You should make separate controllers for each resource. What those two controllers may share is potentially a PostService or other mechanism for getting posts. Also keep in mind if you are using Entity Framework or some other ORM, the Posts may be exposed on the Blog object through a relationship. Therefore your action might be as simple as:
public async Task<IActionResult> GetPostsForBlog(int blogId)
{
return Ok(_context.Blogs.Find(blogId).Posts);
}
I would choose one of both ways and stick with it.
For example, if you are going to pass parameters in the path:
BlogsController
PostsController
CommentsController
There is a difference between two approaches and it's about performance issues on database side.
When use /api/blogs/123/posts
you need to include Posts
in Blog
object like this:
_dbContext.Blogs
.AsNoTracking()
.Include(x => x.Posts)
.Where(x => x.Id == 123)
.FirstOrDefaultAsync();
And this will be translated to T-Sql
and use Inner Join
or Left Join
(based on required relations) between blog table and post table some thing like this:
Select * from (Select top 1 * from dbo.Blogs where Id = 123) b
Left Join dbo.Posts p on b.Id = p.BlogId
But when call /api/posts?blogId=123
you can simply query on Post
object like this:
_dbContext.Posts
.AsNoTracking()
.Where(x => x.BlogId == 123)
.ToListAsync();
And this will be translated to T-Sql
like this:
Select * from dbo.Posts where BlogId=123
As you may be guessed the second query is faster than first.
If you want to follow Rest Api
standard you should follow the first one but for get better performance use second query for get Posts
items and no need implement two approaches, just pick one because with implement two approaches already you implement logic in two places and if logic changed you should change two place of your code.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.