简体   繁体   中英

How should I implement REST API Controller in ASP.NET Core for the resource that has multiple URI endpoints?

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:

  • /api/posts?blogId=123
  • /api/blogs/123/posts

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

  • /api/blogs
  • /api/blogs/{blogId}

PostsController

  • /api/posts
  • /api/posts/{postId}
  • /api/blogs/{blogId}/posts

CommentsController

  • /api/comments
  • /api/comments/{commentId}
  • /api/posts/{postId}/comments

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM