【问题标题】:Design RESTful API using HAL - serialize model relationships使用 HAL 设计 RESTful API - 序列化模型关系
【发布时间】:2013-04-12 23:59:42
【问题描述】:

我对 REST 比较陌生,但我一直在做关于 RESTful 应该如何做的功课。现在,我正在尝试为与其他模型有关系的模型创建一个实现 JSON+HAL 序列化程序的 RESTful api。
python中的示例模型:

class Category(Model):
    name = CharField()
    parent = ManyToOneField(Category)
    categories = OneToManyField(Category)
    products = ManyToManyField(Product)

class Product(Model):
    name = CharField()
    price = DecimalField()
    related = ManyToManyField(Product)
    categories = ManyToManyField(Category)

假设我们有一个类别“目录”,其中包含一个子类别“食物”,其中的产品“汉堡”和“热狗”都是相关的。
第一个问题。类别和产品应该是资源,因此它们需要一个 URI,我应该在我的模型中实现一个 uri 字段并将其存储在数据库中还是以某种方式在运行时计算它,多个标识符(URI)呢?
第二个问题。 可发现性,在 Hal 格式中,“GET /”和不同的节点应该返回什么以使 api 易于自我发现。

{
  "_links":{
    "self":{
      "href":"/"
    },
    "categories":[
      {
        "href":"/catalog"
      }
    ]
  }
}

第三个问题。添加为属性、嵌入或链接。示例“GET /catalog/food”:

{
  "_links":{
    "self":{
      "href":"/catalog/food"
    }
  },
  "name":"food",
  "parent":"/catalog",
  "categories":[],
  "products":[
    "/products/burger",
    "/products/hot-dog"
  ]
}

{
  "_links":{
    "self":{
      "href":"/catalog/food"
    },
    "parent":{
      "href":"/catalog"
    },
    "categories":[

    ],
    "products":[
      {
        "href":"/products/burger"
      },
      {
        "href":"/products/hot-dog"
      }
    ]
  },
  "name":"food"
}

{
  "_links":{
    "self":{
      "href":"/catalog/food"
    }
  },
  "name":"food",
  "_embedded":{
    "parent":{
      "_links":{
        "self":{
          "href":"/catalog"
        }
      },
      "name":"catalog",
      ...
    },
    "categories":[

    ],
    "products":[
      {
        "_links":{
          "self":{
            "href":"/products/burger"
          }
        },
        "name":"burger",
        ...
      },
      {
        "_links":{
          "self":{
            "href":"/products/hot-dog"
          }
        },
        "name":"hot-dog",
        ...
      }
    ]
  }
}

第四个问题。返回结构时我应该走多深。示例“GET /目录

{
  "_links":{
    "self":{
      "href":"/catalog"
    }
  },
  "name":"catalog",
  "parent":null,
  "categories":[
    {
      "name":"food",
      "parent":{...},
      "categories":[],
      "products":[
        {
          "name":"burger",
          "price":"",
          "categories":[...],
          "related":[...]
        },
        {
          "name":"hot-dog",
          "price":"",
          "categories":[...],
          "related":[...]
        }
      ]
    }
  ],
  "products": []
}

【问题讨论】:

  • 那么你最后是怎么做的?

标签: json rest serialization hateoas hal-json


【解决方案1】:

关于第一个问题:我不会将 URI 存储在数据库中。您可以在运行时在控制器内部轻松计算它们,并且控制器有责任关心 URI。通过这种方式,您可以保持模型和 API 解耦,并且如果您决定将来更改 API 结构,则无需使用新的 URI 更新整个数据库。

关于多个标识符,我不确定问题是什么,但在我看来,这与数据库无关,它是路由和控制器应该关心如何处理任何 URI。

关于第二个问题:首先,作为旁注:我会将 categories 这个词保留为 URI 的一部分。例如,我有http://domain.com/api/categories/catalog/food。这样,您可以让您的 API 更具描述性和“可破解性”,这意味着用户应该能够删除 /catalog/food 部分并期望收到包含所有可用类别的集合。

现在,关于 GET 应该返回什么以允许可发现性:我认为您的 URI 结构已经清楚地表明了这一点。当用户点击GET /categories时,他希望获得一个包含类别的列表(每个类别的名称和URI,以保持轻量级),并且当他遵循GET /categories/catalog之类的URI之一时,他应该收到资源catalog是一个类别。同样,当他想GET /products/burger 时,他应该会收到一个产品资源,其中包含您在模型中拥有的所有属性。您可能需要检查this example 以了解您的回复结构。

关于第三个问题:同样,the same example 可以帮助您形成结构。我认为您的第二种响应方式更接近于此,但我还会添加一个name 字段,而不仅仅是href

关于第 4 个问题:当GET 请求需要一个资源集合时(如GET /categories),我建议只为每个资源提供必要的资源,即名称和 URI对于每一个并且只有当用户遵循所需的URI时,他才能收到其余信息。

在你的例子中,catalog 是一个资源,所以GET /categories/catalog 我当然会包括资源(目录)的name 及其自身链接,以及parentsub-categories 和@ 987654339@ 与它相关,我只提供每个名称和 URI,以保持简洁。 但是:这是设计 API 的一般想法。在您的实际问题中,应该根据您的具体业务问题来决定。我的意思是,如果您的 API 是关于包含类别和菜肴的餐厅菜单,您可能希望包含价格或简短描述,即使响应的不是实际产品而是产品集合,因为可能对于您的用户来说,这就是一个重要的信息。因此,一般而言,在响应资源列表时提供所有必要的信息(您只知道这些是针对您的问题的),并在响应特定资源时提供资源的所有详细信息。

【讨论】:

    【解决方案2】:
    1. 我会在数据库中存储一些东西并在运行时计算 URI。这样,如果您移动盒子,它就不是静态的。
    2. 创建一个“书签”页面。我们创建的页面只是一个带有它们的链接的列表。我相信 HAL 明确定义了这一点。书签页面是其他页面需要了解的唯一页面
    3. 对此不确定
    4. 您能走多远由您决定。现在,在我的工作地点,关于细粒与粗粒的争论很大。我打算用小资源做细粒度以保持 api 简单,然后使用扩展能力概念。它是 Subbu 的 REST 书第 35 页中定义的复合资源概念和 Netflix 使用的扩展概念的结合。 http://developer.netflix.com/docs/REST_API_Conventions

    【讨论】:

      猜你喜欢
      • 2020-05-03
      • 2019-06-05
      • 1970-01-01
      • 1970-01-01
      • 2017-05-09
      • 1970-01-01
      • 2014-10-21
      • 1970-01-01
      • 2017-12-14
      相关资源
      最近更新 更多