C# 如何在ASP.NET核心中为具有多个URI端点的资源实现REST API控制器?
假设我正在建模bloggingrestapi,它有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访问
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
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}
- /空气污染指数/职位
- /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获取相关文章。谢谢你的观点。