【问题标题】:Avoiding building (REST API) URLs on the frontend避免在前端构建 (REST API) URL
【发布时间】:2021-02-02 10:08:49
【问题描述】:

我从事后端工作已经有一段时间了,最​​近才开始在前端工作,这让我更接近端到端的 REST 实现。

更重要的是,REST 的一个重要原则是使其可发现和一致,以便客户端知道如何通用地处理资源(HATEOAS、JsonApi 等)。 我一直在阅读this Google article 并且有以下几点:

如果 API 简单直接地使用 HTTP,它只需要记录三四件事。 (如果一个 API 需要您阅读大量文档来学习如何使用它,那么它可能没有使用 HTTP 作为统一 API。) HTTP API 的四个元素是:

  1. 数量有限的固定知名 URL。这些类似于数据库中表的名称。对于可选的额外功劳,请让所有固定网址都可以从一个网址中发现。

以后……

也缺乏了解如何设计好的 HTTP/REST API 的人。不幸的是,我们看到许多尝试采用面向实体的 HTTP/REST 样式的 API 示例,但由于没有始终如一地遵循模型,因此未能实现所有好处。一些常见的错误是:

  1. 使用“本地标识符”而不是 URL 来编码实体之间的引用。如果 API 需要客户端替换 URI 模板中的变量来形成资源的 URL,那么它已经失去了 HTTP 统一接口价值的重要部分。构建对查询进行编码的 URL 是 URI 模板的唯一常见用途,它与 HTTP 作为统一接口的理念兼容。

我都同意,但我不知道如何实现这一点

所以这是场景:

  • API 端点:
    • GET openapi.json / wadl / whatever-discovery-mechanism
      /articles/
      /articles/$id - only for Option 2 below
      ... (maybe for each entity operation in case of exhaustive discovery like openapi,
           but I'd want to keep it minimal for now)
      
    • GET /articles
      {
          data: [
              {
                  title: "Article 1",
                  links: {
                      self: "/articles/1",
                      ...
                  }
              }
          ]
      }
      
    • GET /articles/$id
    • DELETE /articles/$id
    • ...
  • 前端 URL:
    • GET /site/articles - 显示文章列表/表格的页面
    • GET /site/articles/1 - 带有用于编辑该文章的表单的页面

当导航到/site/articles 时,前端会知道调用/articles API 端点——这是谷歌提到的“有限固定网址”之一。 考虑到文章实体返回的链接,也可以删除/更新。 通过客户端导航,前端还可以“重定向”到/site/articles/1

棘手的部分是当用户直接导航到 /site/articles/1 时 - 页面如何知道调用 /articles/$id 而无需构建 URL 本身(或以某种方式翻译它)?

这些是我看到的选项:

  1. 构建URL(这基本上是上面提到的“常见错误1”)
    // /site/articles/1
    const apiUrl = '/articles/' + location.pathname.split('/')[3]
    // /articles/1
    
  2. 从发现链接构建 URL(先前选项的变体,仍然与 IMO 一样糟糕)
    // /site/articles/1
    const apiUrl = api.endpoints.articles + location.pathname.split('/')[3] // or replace or whatever
    // /articles/1
    
  3. 在前端 URL 中对实体“self”链接进行编码
    // /site/articles/L2FydGljbGVzLzE=
    const apiUrl = atob(location.pathname.split('/')[3])
    // /articles/1
    
    我对此的担忧是:
    • 有点丑
    • 不安全(xsrf / 打开重定向 ...)
    • 它强制前端无论如何构建 URL(只有它理解)
  4. 编码实体标识符(我认为它是“自我”链接),然后在/articles 中查找它,然后调用返回的链接
    // /site/articles/L2FydGljbGVzLzE=
    const entityId = atob(location.pathname.split('/')[3])
    const apiUrl = api.get(api.endpoints.articles)
        .first(article => article.links.self === entityId)
        .links.self
    // /articles/1
    
    • 比3还要丑。????
    • 足够安全
    • 乍一看似乎毫无意义...

【问题讨论】:

    标签: api rest architecture uniform-interface


    【解决方案1】:

    如果您担心用户通过输入 URL 直接导航到页面,那么这是固定的知名 URL 之一。很可能任何“可添加书签”的内容都会出现在该列表中。

    这里的关键是短语“编码实体之间的引用”。这不是实体之间的链接,它是一个初始入口点,因此可以从头开始构建 URL。这是一种不灵活的做法,但作为入口点,您别无选择。当您浏览关系时,“常见错误”将在整个应用程序中嵌入“URL 构建”。 IE。通过用户 ID 对文章提供“评论者”列表,并通过将路径耦合到文章页面中的用户来构建这些 URL。

    【讨论】:

    • 同意...但那时的问题是如何将前端 url (/site/articles/1) 映射到 api 资源 (/articles/1),就像有一个这样做的“正确方法”,还要考虑众所周知的 url 部分。
    • 这里的关键是短语“编码实体之间的引用”。这不是实体之间的链接,它是一个初始入口点,因此可以从头开始构建 URL。这是一种不灵活的做法,但作为入口点,您别无选择。当您浏览关系时,“常见错误”将在整个应用程序中嵌入“URL 构建”。 IE。通过用户 ID 对文章提供“评论者”列表,并通过将路径耦合到文章页面中的用户来构建这些 URL。
    • 对我来说听起来不错,你能把评论放在答案中吗?
    猜你喜欢
    • 2021-08-08
    • 2015-04-23
    • 2017-10-25
    • 2021-12-25
    • 2019-06-08
    • 1970-01-01
    • 2019-06-06
    • 2021-05-17
    • 2015-02-12
    相关资源
    最近更新 更多