C# 如何在ASP.NET核心中为具有多个URI端点的资源实现REST API控制器?

C# 如何在ASP.NET核心中为具有多个URI端点的资源实现REST API控制器?,c#,.net,rest,http,asp.net-core,C#,.net,Rest,Http,Asp.net Core,假设我正在建模bloggingrestapi,它有Blog、Post和Comment等资源。 然后,我为Blog和Post资源添加了以下URI: /api/blogs /api/blogs/{blogId} /api/blogs/{blogId}/posts 由于应该避免深度嵌套,我为所有Posts创建了单独的端点,以便获得它们的注释: /api/posts /api/posts/{postId} /api/posts/{postId}/comments 现在,由于可以从两个不同的URI访问

假设我正在建模bloggingrestapi,它有
Blog
Post
Comment
等资源。 然后,我为
Blog
Post
资源添加了以下URI:

/api/blogs
/api/blogs/{blogId}
/api/blogs/{blogId}/posts
由于应该避免深度嵌套,我为所有
Post
s创建了单独的端点,以便获得它们的注释:

/api/posts
/api/posts/{postId}
/api/posts/{postId}/comments
现在,由于可以从两个不同的URI访问
Post
资源,如下所示:

 _dbContext.Blogs
 .AsNoTracking()
 .Include(x => x.Posts)
 .Where(x => x.Id == 123)
 .FirstOrDefaultAsync();
Select * from (Select top 1 * from dbo.Blogs where Id = 123) b 
Left Join dbo.Posts p on b.Id = p.BlogId 
_dbContext.Posts
.AsNoTracking()
.Where(x => x.BlogId == 123)
.ToListAsync();
Select * from dbo.Posts where BlogId=123
  • /api/posts?blogId=123
  • /api/blogs/123/posts
我应该如何在ASP.NET核心API项目中实现这一点,而不需要不必要的代码复制

例如,我应该在同一控制器的相同操作中实现这两个端点,还是应该在两个控制器中分离这两个端点(例如,在
PostsController
中处理
/api/posts?blogId=123
,在
BlogsController
中处理
/api/blogs/123/posts
)? 另外,我应该在两个端点上执行
POST
PUT
DELETE
操作,还是只选择一个作为主要URI


在我了解了如何做到这一点后,是否可以将相同的方法推广到具有相同关系的其他资源(例如,
Post
Comment
)?

您应该为每个资源创建单独的控制器。这两个控制器可能共享的是POST服务或其他获取POST的机制。还请记住,如果您使用的是实体框架或其他一些ORM,那么日志可能会通过关系公开在Blog对象上。因此,您的操作可能非常简单:

public async Task<IActionResult> GetPostsForBlog(int blogId)
{
    return Ok(_context.Blogs.Find(blogId).Posts);
}
public异步任务GetPostsForBlog(int-blogId)
{
返回Ok(_context.Blogs.Find(blogId.Posts));
}

我会选择两种方式中的一种,并坚持下去

例如,如果要在路径中传递参数:

BlogsController

  • /api/博客
  • /api/blogs/{blogId}
PostsController

  • /空气污染指数/职位
  • /api/posts/{postId}
  • /api/blogs/{blogId}/posts
评论控制器

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

这两种方法之间存在差异,主要是关于数据库方面的性能问题

使用
/api/blogs/123/posts
时,需要在
Blog
对象中包含
posts
,如下所示:

 _dbContext.Blogs
 .AsNoTracking()
 .Include(x => x.Posts)
 .Where(x => x.Id == 123)
 .FirstOrDefaultAsync();
Select * from (Select top 1 * from dbo.Blogs where Id = 123) b 
Left Join dbo.Posts p on b.Id = p.BlogId 
_dbContext.Posts
.AsNoTracking()
.Where(x => x.BlogId == 123)
.ToListAsync();
Select * from dbo.Posts where BlogId=123
这将被转换为
T-Sql
,并在blog表和post表之间使用
内部连接
左连接
(基于所需的关系),如下所示:

 _dbContext.Blogs
 .AsNoTracking()
 .Include(x => x.Posts)
 .Where(x => x.Id == 123)
 .FirstOrDefaultAsync();
Select * from (Select top 1 * from dbo.Blogs where Id = 123) b 
Left Join dbo.Posts p on b.Id = p.BlogId 
_dbContext.Posts
.AsNoTracking()
.Where(x => x.BlogId == 123)
.ToListAsync();
Select * from dbo.Posts where BlogId=123
但是当调用
/api/posts?blogId=123
时,您可以像这样简单地查询
Post
对象:

 _dbContext.Blogs
 .AsNoTracking()
 .Include(x => x.Posts)
 .Where(x => x.Id == 123)
 .FirstOrDefaultAsync();
Select * from (Select top 1 * from dbo.Blogs where Id = 123) b 
Left Join dbo.Posts p on b.Id = p.BlogId 
_dbContext.Posts
.AsNoTracking()
.Where(x => x.BlogId == 123)
.ToListAsync();
Select * from dbo.Posts where BlogId=123
这将转换为
T-Sql
,如下所示:

 _dbContext.Blogs
 .AsNoTracking()
 .Include(x => x.Posts)
 .Where(x => x.Id == 123)
 .FirstOrDefaultAsync();
Select * from (Select top 1 * from dbo.Blogs where Id = 123) b 
Left Join dbo.Posts p on b.Id = p.BlogId 
_dbContext.Posts
.AsNoTracking()
.Where(x => x.BlogId == 123)
.ToListAsync();
Select * from dbo.Posts where BlogId=123
正如您可能猜到的,第二个查询比第一个查询快


如果您想遵循Rest Api标准,您应该遵循第一个标准,但为了获得更好的性能,请使用第二个查询来获取
帖子
项,无需实现两种方法,只需选择一个,因为使用实现两种方法,您已经在两个位置实现了逻辑,如果逻辑发生了变化,您应该更改代码的两个位置。

我应该将此
GetPostsForBlog(intBlogId)
方法放在哪个控制器中?是否应该去<代码> PostsController <代码>或<代码> BurgsSuthor <代码>(因为它们都可以返回数组的位置)?当然,在POST控件中,您也应该考虑到我们只讨论了[HtpGET]请求到这一点。考虑[HtpStest]或[HtpDele]方法的去向。例如,对我来说,[HttpPost]方法更可能在posts控制器上,因为这有点奇怪(但并非不可能)要将Posts集合作为对Blogs控制器的呼叫的一部分进行操作,请为这两个控制器提供最小但完整的示例代码,以便我选择您的答案。谢谢标准是让CommentsController具有/api/comments子例程。您必须覆盖此方法的约定。这不一定是件坏事,但我想指出的是,这与标准有所不同。关于数据库部分,有一个很好的教程使用了
Blog
Post
:对于API部分,您可以在这里找到灵感:Sidenote:
FirstOrDefaultAsync()
结果是
SELECT TOP(1)…
@momo是的,你说得对。但我只是展示了数据库中发生的事情,我知道sql会从blog表中选择最上面的一个,并使用join获取相关文章。谢谢你的观点。