REST API - 如何在POST方法中添加(如果不存在)相关记录?

时间:2017-04-05 19:15:24

标签: c# rest asp.net-web-api restful-architecture api-design

数据模型

enter image description here

实体

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,并编写了一个通用存储库,它针对PostCategory等单个实体执行CRUD操作,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                   ║
╚═══════╩═══════════════════════╩════════════════════════════════════╝

情况

当我通过针对Post端点执行POST请求创建/api/posts实体并且我将正文中的Post实体数据作为JSON时,它可能会拥有1个或更多标记实例以及01类别。

示例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

如果类别已填写,则应插入该类别(如果不存在)。

问题

考虑到这种情况,我如何针对POST端点发出/api/posts请求,并确保相关的TagCategory实体在添加时添加存在吗?

如果上面显示的示例帖子发布到此端点,如何处理添加不存在的标记,如果不存在,则添加类别,然后添加新帖子?

另外,我在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();
        }
    }

2 个答案:

答案 0 :(得分:1)

我过去做过类似的事情,我做的就是数据库。当调用像“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)

答案 1 :(得分:1)

示例解决方案可以在这里:

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();

            }