Active Model Serializer和Pundit在显示CRUD操作期间删除记录

好的,这里有些东西被严重破坏了……

我正在为我的Rails 5 JSONAPI服务器使用Active Model Serializer和Pundit,为我的前端应用程序使用Ember。

我有User模型和User模型的Pundit策略,可以防止非作者查看未发表的故事和章节。

目前,我看到一个奇怪的问题,如下所示:

 1. UserA creates StoryA, and two published chapters Chapter1 and Chapter2 2. UserA then creates two unpublished chapters Chapter3 and Chapter4 3. UserA logouts 4. UserB logins 5. UserB views the same story created by UserA 6. Server policy kicks in and scope the results to only published chapters since UserB isn't the author. 7. * An SQL DELETE query is sent to delete the two unpublished stories for some odd reason. 

这是一些截图:

UserA创建了2个已发布的故事和2个未发布的故事

截图1

数据库记录显示4个故事属于Mount Targon故事

截图2

UserA注销并且UserB登录,查看Mount Targon的故事

截图3

(如您所见,UserB只看到两个发布的章节,这些章节是正确的但是……)

由于某些奇怪的原因,未发布的章节将从数据库中删除

截图4

查看Rails控制台,我在ChaptersController#show #show CRUD操作期间看到DELETE查询:

 Started GET "/stories/16" for 127.0.0.1 at 2017-11-05 17:02:53 +0800 Processing by StoriesController#show as JSON Parameters: {"id"=>"16"} User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] Story Load (0.1ms) SELECT "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT ? [["id", 16], ["LIMIT", 1]] Chapter Load (0.3ms) SELECT "chapters".* FROM "chapters" INNER JOIN "stories" ON "stories"."id" = "chapters"."story_id" INNER JOIN "users" ON "users"."id" = "stories"."user_id" WHERE "chapters"."story_id" = ? AND ((stories.published = 't' AND chapters.published = 't') OR stories.user_id = 2) [["story_id", 16]] Chapter Load (0.1ms) SELECT "chapters".* FROM "chapters" WHERE "chapters"."story_id" = ? [["story_id", 16]] (0.1ms) begin transaction Started GET "/chapters/26" for 127.0.0.1 at 2017-11-05 17:02:53 +0800 SQL (0.3ms) DELETE FROM "chapters" WHERE "chapters"."id" = ? [["id", 32]] Started GET "/chapters/27" for 127.0.0.1 at 2017-11-05 17:02:53 +0800 Processing by ChaptersController#show as JSON SQL (0.1ms) DELETE FROM "chapters" WHERE "chapters"."id" = ? [["id", 33]] Processing by ChaptersController#show as JSON Parameters: {"id"=>"26"} (2.1ms) commit transaction Parameters: {"id"=>"27"} User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] [active_model_serializers] User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] Chapter Load (0.1ms) SELECT "chapters".* FROM "chapters" WHERE "chapters"."id" = ? LIMIT ? [["id", 26], ["LIMIT", 1]] [active_model_serializers] Story Load (0.4ms) SELECT "stories".* FROM "stories" WHERE "stories"."user_id" = ? [["user_id", 1]] Chapter Load (0.2ms) SELECT "chapters".* FROM "chapters" WHERE "chapters"."id" = ? LIMIT ? [["id", 27], ["LIMIT", 1]] Started GET "/chapters/32" for 127.0.0.1 at 2017-11-05 17:02:53 +0800 Started GET "/chapters/33" for 127.0.0.1 at 2017-11-05 17:02:53 +0800 Story Load (0.2ms) SELECT "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT ? [["id", 16], ["LIMIT", 1]] [active_model_serializers] Rendered StorySerializer with ActiveModelSerializers::Adapter::JsonApi (22.64ms) Story Load (0.1ms) SELECT "stories".* FROM "stories" WHERE "stories"."id" = ? LIMIT ? [["id", 16], ["LIMIT", 1]] Processing by ChaptersController#show as JSON Processing by ChaptersController#show as JSON [active_model_serializers] Rendered ChapterSerializer with ActiveModelSerializers::Adapter::JsonApi (0.82ms) Completed 200 OK in 43ms (Views: 27.1ms | ActiveRecord: 3.9ms) 

我的ChaptersController显示操作中甚至没有“删除”或“销毁”这个词……那么记录如何被删除?

 # CHAPTERS CONTROLLER def show chapter = Chapter.find_by(id: params[:id]) if chapter.present? authorize chapter render json: chapter, status: :ok else skip_authorization render json: { error: "Chapter not found" }, status: :not_found end end 

我的章节政策显示方法:

 # CHAPTER PUNDIT POLICY def show? (@record.published? && @record.story.published?) || (@record.story.user == @user) end 

我的StoriesController显示动作如下:

 # STORIES CONTROLLER def show story = Story.find_by(id: params[:id]) if story.present? authorize story story.chapters = policy_scope(story.chapters) render json: story, include: [:user, :chapters], status: :ok else skip_authorization render json: { errors: "Story not found" }, status: :not_found end end 

我认为可能是Ember在幕后做了一些有趣的额外查询,但我使用Postman Mac应用程序测试查看故事,果然,未发表的章节被删除而不经过Ember。 由于一些奇怪的原因它正在发生在服务器端= /

有任何想法吗?

在我看来,这实际上是在StoriesController#show中完成的。 您应该再次尝试仅测试此方法(在RSpec或其他方面),并且您还可以单步执行代码以精确查明删除发生的位置。 我的猜测是:

 story.chapters = policy_scope(story.chapters) 

你在这里改变故事章节。 想必你想做点什么

 @displayed_chapters = policy_scope(story.chapters) 

没有任何东西可以作为DELETE的可能来源脱颖而出。 我认为解决方案是一些好的,老式的调试。

  • 尝试评论动作中的所有行。 然后取消注释一个。
  • 查找日志中提到的事务
  • 订阅sql查询并在查询删除时引发并查看堆栈跟踪。 或者完全覆盖删除。

如果删除是在序列化程序或策略中发生的,那么用户态代码就会发生这种情况。 两个图书馆都不知道有效的记录。