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 操作,例如 PostCategoryTag

一切正常。

我正在编写一个 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 或更多标签实例,以及 01 类别。

示例 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 请求,并确保添加相关的 TagCategory 实体(如果它们存在)不存在?

如果上面显示的示例 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();

            }