REST API - 如何在 POST 方法中添加(如果不存在)相关记录?
REST API - How To ADD (If Not Exists) Related Records in POST Method?
数据模型
实体
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string UrlSlug { get; set; }
public string TnImage { get; set; }
public string Author { get; set; }
public List<Tag> Tags { get; set; }
public Category Category { get; set; }
public string DatePublished { get; set; }
public string DateCreated { get; set; }
public string DateModified { get; set; }
public string Description { get; set; }
public string ArticleBody { get; set; }
}
我正在针对 Sql Server Compact 嵌入式数据库使用 Entity-Framework,并且编写了一个 Generic Repository 来针对单个实体执行 CRUD 操作,例如 Post
、Category
、Tag
等
一切正常。
我正在编写一个 ASP.Net Web API 以通过 REST-ful API.
公开 CRUD
REST API 终点
╔═══════╦═══════════════════════╦════════════════════════════════════╗
║ VERB ║ End Point ║ Description ║
╠═══════╬═══════════════════════╬════════════════════════════════════╣
║ GET ║ /api/tags ║ Returns all Tags ║
║ GET ║ /api/tags/tips ║ Returns single Tag (name=tips) ║
║ POST ║ /api/tags ║ Creates new Tag ║
║ GET ║ /api/categories ║ Returns all Categories ║
║ GET ║ /api/categories/news ║ Returns single Category (name=news)║
║ POST ║ /api/categories ║ Creates new Category ║
║ GET ║ /api/posts ║ Returns all Post ║
║ GET ║ /api/posts/51 ║ Returns single Post (Id=51) ║
║ GET ║ /api/posts/2/Tags ║ Returns Tags for Post w/ Id=2 ║
║ GET ║ /api/posts/2/Category ║ Returns Category for Post w/ Id=2 ║
║ POST ║ /api/posts ║ Creates new Post ║
╚═══════╩═══════════════════════╩════════════════════════════════════╝
情况
当我通过对 /api/posts
端点执行 POST
请求创建 Post
实体时,我将 Post
实体数据作为 [=71] =],它可能有 1
或更多标签实例,以及 0
或 1
类别。
示例 POST
博客正文 Post
{
"title": "How to get 6 Pack Abs in 6 days",
"urlSlug": "6-pack-abs-6-days",
"tnImage": "http:\/\/example.com\/image.jpg",
"author": "John Doe",
"tags": [
{
"name": "6 pack abs tips"
},
{
"name": "exercise tips"
},
{
"name": "workout videos"
}
],
"category": {
"name": "fitness"
},
"datePublished": "2017-04-01",
"dateCreated": "2015-01-20",
"dateModified": "2017-04-01",
"description": "SEO keyword stuffed description for fake tips to get abs here",
"articleBody": "full post body containing fake tips to get 6 packs. html"
}
在这些标签中,有些可能存在,有些可能不存在。那些不存在的需要被创造。对于需要创建的标签,UrlSlug
将使用辅助方法生成。
类别,如果填写,如果不存在,应该插入。
问题
在这种情况下,我如何对 /api/posts
端点发出 POST
请求,并确保添加相关的 Tag
和 Category
实体(如果它们存在)不存在?
如果上面显示的示例 post posted 到此端点,我如何处理添加不存在的标签、类别(如果不存在),然后添加新 post?
另外,我是不是在 REST 设计方面做错了?如果是,推荐的方法是什么来确保数据不一致,即添加一些标签然后发生错误,所以现在有孤儿。
public class PostsController : ApiController
{
[Route("api/posts")]
[HttpPost]
public IHttpActionResult Post([FromBody]PostDto post)
{
try
{
if (post == null)
{
return BadRequest();
}
// what goes in here, so that I add tags that don't exist,
// category if it doesn't exist
// and then create the post. All ADD methods will call repo methods
//
// Also is there some other way, i.e. am I approaching this wrong?
}
catch (Exception)
{
return InternalServerError();
}
}
我过去做过类似的事情,我做的地方是在数据库中。当调用像 "SetTag" 这样的通用过程时,它会获取 entityid 和标签,然后首先进行检查查询,如:
DECLARE @TagID INT
SELECT @TagID = TagID FROM TagTable WHERE Tag = @TagPassedIn
IF @TagID IS NULL
BEGIN
INSERT INTO TagTable (Tag) VALUES (@TagPassedIn)
SELECT @TagID = SCOPE_INDENTITY()
END
INSERT INTO AttTable (EntityID,TagID) VALUES (@EntityIDPassedIn,@TagID)
示例解决方案可以在这里:
using (var context = new YourContext())
{
//Find or Create Category
Category category = null;
if (context.Categories.Any(cat => cat.Name == post.category.Name))
{
category = context.Categories.FirstOrDefault(cat => cat.Name == post.category.Name);
}
else
{
category = new Category
{
Name = post.category.Name
};
context.Categories.Add(category);
}
//Find or Create Tags
var tags = new List<Tag>();
foreach (var tag in post.tags)
{
Tag targetedTag;
if (context.Tags.Any(t => t.Name == tag.Name))
{
targetedTag = context.Tags.FirstOrDefault(t => t.Name == tag.Name);
}
else
{
targetedTag = new Tag
{
Name = tag.Name,
// UrlSlug = use helper as you've said
};
context.Tags.Add(targetedTag);
}
tags.Add(targetedTag);
}
var targetedPost = new Post
{
Category = category,
Tags = tags,
ArticleBody = post.articleBody,
Author = post.author,
DateCreated = post.dateCreated,
DateModified = post.dateModified,
DatePublished = post.datePublished,
Description = post.description,
Title = post.title,
TnImage = post.tnImage,
UrlSlug = post.urlSlug
};
context.Posts.Add(targetedPost);
context.SaveChanges();
}
数据模型
实体
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string UrlSlug { get; set; }
public string TnImage { get; set; }
public string Author { get; set; }
public List<Tag> Tags { get; set; }
public Category Category { get; set; }
public string DatePublished { get; set; }
public string DateCreated { get; set; }
public string DateModified { get; set; }
public string Description { get; set; }
public string ArticleBody { get; set; }
}
我正在针对 Sql Server Compact 嵌入式数据库使用 Entity-Framework,并且编写了一个 Generic Repository 来针对单个实体执行 CRUD 操作,例如 Post
、Category
、Tag
等
一切正常。
我正在编写一个 ASP.Net Web API 以通过 REST-ful API.
公开 CRUDREST API 终点
╔═══════╦═══════════════════════╦════════════════════════════════════╗
║ VERB ║ End Point ║ Description ║
╠═══════╬═══════════════════════╬════════════════════════════════════╣
║ GET ║ /api/tags ║ Returns all Tags ║
║ GET ║ /api/tags/tips ║ Returns single Tag (name=tips) ║
║ POST ║ /api/tags ║ Creates new Tag ║
║ GET ║ /api/categories ║ Returns all Categories ║
║ GET ║ /api/categories/news ║ Returns single Category (name=news)║
║ POST ║ /api/categories ║ Creates new Category ║
║ GET ║ /api/posts ║ Returns all Post ║
║ GET ║ /api/posts/51 ║ Returns single Post (Id=51) ║
║ GET ║ /api/posts/2/Tags ║ Returns Tags for Post w/ Id=2 ║
║ GET ║ /api/posts/2/Category ║ Returns Category for Post w/ Id=2 ║
║ POST ║ /api/posts ║ Creates new Post ║
╚═══════╩═══════════════════════╩════════════════════════════════════╝
情况
当我通过对 /api/posts
端点执行 POST
请求创建 Post
实体时,我将 Post
实体数据作为 [=71] =],它可能有 1
或更多标签实例,以及 0
或 1
类别。
示例 POST
博客正文 Post
{
"title": "How to get 6 Pack Abs in 6 days",
"urlSlug": "6-pack-abs-6-days",
"tnImage": "http:\/\/example.com\/image.jpg",
"author": "John Doe",
"tags": [
{
"name": "6 pack abs tips"
},
{
"name": "exercise tips"
},
{
"name": "workout videos"
}
],
"category": {
"name": "fitness"
},
"datePublished": "2017-04-01",
"dateCreated": "2015-01-20",
"dateModified": "2017-04-01",
"description": "SEO keyword stuffed description for fake tips to get abs here",
"articleBody": "full post body containing fake tips to get 6 packs. html"
}
在这些标签中,有些可能存在,有些可能不存在。那些不存在的需要被创造。对于需要创建的标签,UrlSlug
将使用辅助方法生成。
类别,如果填写,如果不存在,应该插入。
问题
在这种情况下,我如何对 /api/posts
端点发出 POST
请求,并确保添加相关的 Tag
和 Category
实体(如果它们存在)不存在?
如果上面显示的示例 post posted 到此端点,我如何处理添加不存在的标签、类别(如果不存在),然后添加新 post?
另外,我是不是在 REST 设计方面做错了?如果是,推荐的方法是什么来确保数据不一致,即添加一些标签然后发生错误,所以现在有孤儿。
public class PostsController : ApiController
{
[Route("api/posts")]
[HttpPost]
public IHttpActionResult Post([FromBody]PostDto post)
{
try
{
if (post == null)
{
return BadRequest();
}
// what goes in here, so that I add tags that don't exist,
// category if it doesn't exist
// and then create the post. All ADD methods will call repo methods
//
// Also is there some other way, i.e. am I approaching this wrong?
}
catch (Exception)
{
return InternalServerError();
}
}
我过去做过类似的事情,我做的地方是在数据库中。当调用像 "SetTag" 这样的通用过程时,它会获取 entityid 和标签,然后首先进行检查查询,如:
DECLARE @TagID INT
SELECT @TagID = TagID FROM TagTable WHERE Tag = @TagPassedIn
IF @TagID IS NULL
BEGIN
INSERT INTO TagTable (Tag) VALUES (@TagPassedIn)
SELECT @TagID = SCOPE_INDENTITY()
END
INSERT INTO AttTable (EntityID,TagID) VALUES (@EntityIDPassedIn,@TagID)
示例解决方案可以在这里:
using (var context = new YourContext())
{
//Find or Create Category
Category category = null;
if (context.Categories.Any(cat => cat.Name == post.category.Name))
{
category = context.Categories.FirstOrDefault(cat => cat.Name == post.category.Name);
}
else
{
category = new Category
{
Name = post.category.Name
};
context.Categories.Add(category);
}
//Find or Create Tags
var tags = new List<Tag>();
foreach (var tag in post.tags)
{
Tag targetedTag;
if (context.Tags.Any(t => t.Name == tag.Name))
{
targetedTag = context.Tags.FirstOrDefault(t => t.Name == tag.Name);
}
else
{
targetedTag = new Tag
{
Name = tag.Name,
// UrlSlug = use helper as you've said
};
context.Tags.Add(targetedTag);
}
tags.Add(targetedTag);
}
var targetedPost = new Post
{
Category = category,
Tags = tags,
ArticleBody = post.articleBody,
Author = post.author,
DateCreated = post.dateCreated,
DateModified = post.dateModified,
DatePublished = post.datePublished,
Description = post.description,
Title = post.title,
TnImage = post.tnImage,
UrlSlug = post.urlSlug
};
context.Posts.Add(targetedPost);
context.SaveChanges();
}