REST如何正确处理关系

时间:2013-12-01 19:28:51

标签: rest restful-architecture

假设我有一个资源(比如Class),它有很多学生资源。

Class
  id: 1
  name: A
  subject: Maths

Student
  name: Foo
  surname: Bar
  class: [?]

我有2个方案需要处理。

[1]:插入新学生

在这种情况下,更好的解决方案是添加一个类ID并将资源发布为

Student
  name: Foo
  surname: Bar
  class: 1

这使得服务器端处理变得非常简单,因为我的资源主要是数据库表镜像。

[2]:列出学生,每个人都有自己的班级。例如:

Student Name | Student Surname | Student Class | Class Subject
Foo1           Bar               A               Math
Foo2           Bar               A               Math
Foo3           Bar               B               Science

这里的id表示不足以显示我需要的每个数据,所以我可以

  • (a)在单独的电话中加载具有班级ID和加载班级数据的学生
  • (b)使用嵌入式班级数据加载学生

在案例(a)中,需要再加一个远程调用来加载班级数据,因为我的学生看起来像

Students: [
  {name: Foo1, surname: Bar, class: 1},
  {name: Foo2, surname: Bar, class: 1},
  {name: Foo3, surname: Bar, class: 2},
]

案例(b)似乎更合适,因为我的学生看起来像

Students: [
  {name: Foo1, surname: Bar, class: {id:1, name: A, subject: Maths}},
  {name: Foo2, surname: Bar, class: {id:1, name: A, subject: Maths}},
  {name: Foo3, surname: Bar, class: {id:2, name: B, subject: Science}},
]

如果我选择(a),我对方案[1]没问题,并且我在方案[2](额外调用填充类数据)方面有开销。

如果我选择(b)我对方案[2]没问题,但我对方案[1]的学生资源表示有问题,因为我应该在有效负载中发布一个完整的Class资源,如下所示:

Student
  name: Foo
  surname: Bar
  class: {id:1, name: B, subject: Science}

问题1:最好的方法是什么?

问题2:你能指出任何有关这方面的好读物吗?

==编辑==

问题3:上面的 RESTful是什么?

2 个答案:

答案 0 :(得分:0)

案例A似乎更好。

  • 您的学生将外键存储到他所在的班级。
  • 更新班级信息很简单,因为学生只存储外键(例如,不需要对所有学生对象进行级联更新)

我建议你学习老式的relational database design。即使你没有使用它们,你也会学到很多关于数据的知识。

答案 1 :(得分:0)

使用您的示例,以下是使用JSON和HAL进行此操作的方法。

根页:

请求:

GET / HTTP/1.1
Host: example.com

<强>响应:

200 OK
Content-Type: application/hal+json

{
  "_links": {
    "self": { "href": "/" },
    "students": { "href": "/students" },
    "classes": { "href": "/classes" },
    "students-with-classes": { "href": "/students?expand" },
  }
}

通过发布到提供的href,可以在students关系中记录创建新学生。据推测,我之前会遵循classes链接关系来获取到A类的链接,以便在添加用户时可以包含它。

请求:

POST /students HTTP/1.1
Content-Type: application/json

{
  name: "Foo",
  surname: "Bar",
  class-link: "/classes/1"
}

<强>响应:

201 Created
Location: http://example.com/students/10

列出学生集合(遵循students-with-classes关系);

请求:

GET /students?expand HTTP/1.1

<强>响应:

200 OK
Content-Type: application/hal+json

{
  "_links": {
    self: { href: "/students" }
  },
  "_embedded": {
    "students": [
      {
        "_links": {
          "self": { "href": "/students/1" },
          "classes": [ { "href": "/classes/1", "title": "A" } ]
        },
        "name": "Foo1",
        "surname": "Bar"
      },
      {
        "_links": {
          "self": { "href": "/students/2" },
          "classes": [ { "href": "/classes/1", "title": "A" } ]
        },
        "name": "Foo2",
        "surname": "Bar"
      },
      {
        "_links": {
          "self": { "href": "/students/3" },
          "classes": [ { "href": "/classes/2", "title": "B" } ]
        },
        "name": "Foo3",
        "surname": "Bar"
      }
    ],
    "classes": [
      {
        "_links": {
          "self": { "href": "/classes/1" }
        },
        "name": "A",
        "subject": "Math"
      },
      {
        "_links": {
          "self": { "href": "/classes/2" }
        },
        "name": "A",
        "subject": "Science"
      }
    ]
  }
}

有几点需要注意:

  • POST通用application/json数据,但检索application/hal+json。每个方向的表示不需要相同。
  • 我使用URI作为要链接的Class的资源标识符,而不是人为的外部ID。这样的ID 可能是该URI的一部分,但客户端永远不应该做出这样的假设。