RESTful API - 处理嵌套关系

RESTful API - Handling nested relations

假设我正在构建一个 API 来处理约会。一个预约包括一名医生、一名患者和一组护理。

如果我想创建约会,POST 数据应该是什么样的?请注意,此查询只会创建约会。数据库中已存在医生、患者和护理条目。

我有两个选择:

短的

{
  doctor_id: 8,
  patient_id: 4,
  cares_ids:
  [1,7]
}

较长的那个

{
  doctor: {
    id: 8,
    name: 'Dr James Brown',
    phone: '107-102-304',
    address: '16th avenue'
  },
  patient: {
    id: 4,
    name: 'Mr Elvis',
    pathology: 'Blah Blah.'
  },
  cares:
  [
    {
      id: 1,
      name: 'Dental cares'
    },
    {
      id: 7,
      name: 'Back pain'
    }
  ]
}

短的感觉更干净,因为我们没有发送无用的数据,如姓名和地址等。但第二个在我的前端代码中感觉更 natural/semantic。

例如在我的应用程序中,我只需要做:

appointment.doctor = selected_doctor
// Instead of doing
appointment.doctor_id = selected_doctor.id

对于这种情况,是否有任何RESTful最佳实践?

其实你面临的是关系问题。 OData 提供了解决此问题的方法,称为 "navigation properties"。也许这可以为您提供一些解决此问题的提示。您可以查看这些 links 了解更多详情:http://www.odata.org/getting-started/basic-tutorial/ and http://www.odata.org/getting-started/advanced-tutorial/

其实这里有几个问题:

  • 设计你想要在预约时拥有的表现形式
  • 设计表示以创建或更新约会
  • 设计表示以分配(单基数 - "doctor" 和 "patient")/添加和删除(多基数 - “关心)link 到约会(例如医生)

我认为你在问题中给出的完整表示对应于你在预约时所期望的,即预约数据包括医生、患者和护理等关系的数据。

这些提示对于 link 预约医生和病人等其他元素并非都是必需的。只有标识符很重要。我认为您可以对约会创建有不同的表示。在此级别设置对元素的引用就足够了。所以你可以有这样的东西:

POST /appointments
{
  doctor-ref: http://.../doctors/8,
  patient-ref: http://.../patients/4,
  cares-ref: [ http://.../cares/1, http://.../cares/7 ]
}

POST /appointments
{
  doctor@metadata.ref: http://.../doctors/8,
  patient@metadata.ref: http://.../patients/4,
  cares@metadata.ref: [ http://.../cares/1, http://.../cares/7 ]
}

以同样的方式,您可以定义额外的资源,以便能够在创建约会后更新这些 link,而无需发送所有内容。我们可以想象这样的事情:

  • 单基数

    PUT /appointments/<appointmentid>/doctor
    { doctor@metadata.ref: http://.../doctors/8 }
    
  • 多基数

    POST /appointments/<appointmentid>/cares
    { metadata.ref: http://.../cares/8 }
    DELETE appointments/<appointmentid>/cares
    { metadata.ref: http://.../cares/8 }
    

您会注意到,您也可以选择不检索某个约会的所有约会数据(如果您对此感兴趣)。以下是您可能拥有的不同表示形式:

  • 完成

    {
      doctor: {
        id: 8,
        name: 'Dr James Brown',
        phone: '107-102-304',
        address: '16th avenue'
      },
      patient: {
        id: 4,
        name: 'Mr Elvis',
        pathology: 'Blah Blah.'
      },
      cares:
        [
          {
            id: 1,
            name: 'Dental cares'
          },
          {
            id: 7,
            name: 'Back pain'
          }
       ]
     }
    

    }

  • 与links/参考文献

    并列
    {
      doctor@metadata.ref: http://.../doctors/8,
      patient: {
        id: 4,
        name: 'Mr Elvis',
        pathology: 'Blah Blah.'
      },
      cares@metadata.ref: [ http://.../cares/1, http://.../cares/7 ]
    }
    

    {
      doctor@metadata.ref: http://.../doctors/8,
      patient: {
        id: 4,
        name: 'Mr Elvis',
        pathology: 'Blah Blah.'
      },
      cares:
        [
          {
            id: 1,
            name: 'Dental cares'
          },
          {
            id: 7,
            name: 'Back pain'
          }
       ]
     }
    

    }

此功能在 OData 中对应于查询参数 $expand

你可以看看这个 link:http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/entity-relations-in-odata-v4。请参阅 "Getting Related Entities"、"Creating a Relationship Between Entities" 和 "Deleting a Relationship Between Entities" 部分。它可以为您的表示提供提示。

问题的最后一部分与构建表示的方式有关(例如创建)。我不确切知道你使用的技术,但在 Java 中,你可以做类似的事情 appointment.doctor = selected_doctor 并稍微调整你的对象的序列化以创建类似的东西 doctor@metadata.ref: http://.../doctors/8医生的例子。例如以 Jackson2, it's called custom serializer (see this link http://www.baeldung.com/jackson-custom-serialization 为例)。

希望对你有帮助, 蒂埃里