HATEOAS:绝对或相对 URL?

发布于 2024-08-20 14:22:20 字数 215 浏览 6 评论 0原文

在使用 HATEOAS 设计 RESTful Web 服务时,将链接显示为完整 URL (“ http://server:port/application/customers/1234") 与仅路径("/application/customers/1234")?

In designing a RESTful Web Service using HATEOAS, what are the pros and cons of showing a link as a complete URL ("http://server:port/application/customers/1234") vs. just the path ("/application/customers/1234")?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

樱花落人离去 2024-08-27 14:22:20

当人们说“相对 URI”时,存在微妙的概念歧义。

根据 RFC3986 的定义,通用 URI 包含:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment

棘手的是,当省略方案和权限,“路径”部分本身可以是绝对路径(以 / 开头)或“无根”相对路径。示例:

  1. 绝对 URI 或完整 URI:"http://example.com:8042/over/there?name=ferret"
  2. 这是一个 相对 uri,带绝对路径/over/there
  3. 这是一个相对 uri,带相对路径此处 或 < code>./here 或 ../here 或等等。

因此,如果问题是“服务器是否应该在静态响应中生成相对路径”,答案是“否”,详细原因是可以在这里找到
我认为大多数人(包括我)反对“相对URI”实际上是反对“相对路径”。

在实践中,大多数服务器端 MVC 框架可以轻松生成带有绝对路径的相对 URI,例如 /absolute/path/to/the/controller,问题就变成了“服务器实现是否应在绝对路径前面添加 scheme://hostname:port 前缀”。就像OP的问题一样。我对此不太确定。

一方面,我仍然认为建议服务器返回完整的 uri。但是,服务器应该永远不要对主机名:端口进行硬编码 源代码中的内容如下(否则我宁愿回退到具有绝对路径的相对 uri)。解决方案是服务器端始终从 HTTP 请求的“Host”标头获取该前缀。但不确定这是否适用于所有情况。

另一方面,客户端将http://example.com:8042和绝对路径连接起来似乎并不是很麻烦。毕竟,客户端在向服务器发送请求时已经知道该方案和域名,对吗?

总而言之,我想说,建议使用绝对 URI,可能会回退到具有绝对路径的相对 URI,切勿使用相对路径

There is a subtle conceptual ambiguity when people say "relative URI".

By RFC3986's definition, a generic URI contains:

  URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

  hier-part   = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment

The tricky thing is, when scheme and authority are omitted, the "path" part itself can be either an absolute path (starts with /) or a "rootless" relative path. Examples:

  1. An absolute URI or a full URI: "http://example.com:8042/over/there?name=ferret"
  2. And this is a relative uri, with absolute path: /over/there
  3. And this is a relative uri, with relative path: here or ./here or ../here or etc.

So, if the question was "whether a server should produce relative path in restful response", the answer is "No" and the detail reason is available here.
I think most people (include me) against "relative URI" are actually against "relative path".

And in practice, most server-side MVC framework can easily generate relative URI with absolute path such as /absolute/path/to/the/controller, and the question becomes "whether the server implementation should prefix a scheme://hostname:port in front of the absolute path". Like the OP's question. I am not quite sure about this one.

On the one hand, I still think server returning a full uri is recommended. However, the server should never hardcode the hostname:port thing inside source code like this (otherwise I would rather fallback to relative uri with absolute path). Solution is server-side always obtaining that prefix from HTTP request's "Host" header. Not sure whether this works for every situations though.

On the other hand, it seems not very troublesome for the client to concatenate the http://example.com:8042 and the absolute path. After all, the client already know that scheme and domain name when it send the request to the server right?

All in all, I would say, recommend to use absolute URI, possibly fallback to relative URI with absolute path, never use relative path.

余厌 2024-08-27 14:22:20

这取决于谁在编写客户端代码。如果您正在编写客户端和服务器,那么这没有太大区别。您要么会遭受在客户端或服务器上构建 URL 的痛苦。

但是,如果您正在构建服务器并且希望其他人编写客户端代码,那么如果您提供完整的 URI,他们会更喜欢您。解析相对 URI 可能有点棘手。首先,如何解决它们取决于返回的媒体类型。 HTML 具有基本标记,XML 可以在每个嵌套元素中具有 xml:base 标记,Atom 提要可以在提要中具有一个基础,在内容中具有不同的基础。如果您没有向客户端提供有关基本 URI 的显式信息,那么他们必须从请求 URI 或从 Content-Location 标头获取基本 URI!并注意后面的斜杠。基本 URI 通过忽略最后一个斜杠右侧的所有字符来确定。这意味着在解析相对 URI 时,尾部斜杠现在非常重要。

唯一需要提及的另一个问题是文档大小。如果您要返回一个大型项目列表,其中每个项目可能有多个链接,则在不压缩实体的情况下,使用绝对 URL 可能会向您的实体添加大量字节。这是一个性能问题,您需要根据具体情况确定它是否重要。

It depends on who is writing the client code. If you are writing the client and server then it doesn't make much difference. You will either suffer the pain of building the URLs on the client or on the server.

However, if you are building the server and you expect other people to write client code then they will love you much more if you provide complete URIs. Resolving relative URIs can be a bit tricky. First how you resolve them depends on the media-type returned. HTML has the base tag, XML can have xml:base tags in every nested element, Atom feeds could have a base in the feed and a different base in the content. If you don't provide your client with explicit information about the base URI then they have to get the base URI from the request URI, or maybe from the Content-Location header! And watch out for that trailing slash. The base URI is determined by ignoring all characters to the right of the last slash. This means that trailing slash is now very significant when resolving relative URIs.

The only other issue that does require a small mention is document size. If you are returning a large list of items where each item may have multiple links, using absolute URLs can add a significant amount of bytes to your entity if you do not compress the entity. This is a performance issue and you need to decide if it is significant on a case by case basis.

陌上芳菲 2024-08-27 14:22:20

唯一真正的区别似乎是,如果客户端使用绝对 URI,而不是从相对版本构建它们,那么对客户端来说会更容易。当然,这种差异足以动摇我制作绝对版本。

The only real difference would seem to be that it's easier for clients if they are consuming absolute URIs instead of having to construct them from the relative version. Of course, that difference would be enough to sway me to do the absolute version.

ぃ弥猫深巷。 2024-08-27 14:22:20

随着应用程序的扩展,您可能希望进行负载平衡、故障转移等。如果您返回绝对 URI,那么您的客户端应用程序将遵循不断变化的服务器配置。

As your application scales, you may wish to do load balancing, fail-over, etc. If you return absolute URIs then your client-side apps will follow your evolving configuration of servers.

紫竹語嫣☆ 2024-08-27 14:22:20

使用 RayLou 的三分法,我的组织选择支持 (2)。主要原因是避免 XSS(跨站脚本)攻击。问题是,如果攻击者可以将自己的 URL 根注入到从服务器返回的响应中,则后续用户请求(例如使用用户名和密码的身份验证请求)可以转发到攻击者自己的服务器*。

有些人提出了能够将请求重定向到其他服务器以实现负载平衡的问题,但是(虽然这不是我的专业领域)我敢打赌,有更好的方法来启用负载平衡,而不必显式地将客户端重定向到不同的服务器主机。

*如果这个推理过程有任何缺陷,请告诉我。当然,目标不是阻止所有攻击,而是至少阻止一种攻击途径。

Using RayLou's trichotomy my organization has opted for favoring (2). The primary reason is to avoid XSS (Cross-Site Scripting) attacks. The issue is, if an attacker can inject their own URL root into the response coming back from the server, then subsequent user requests (such as an authentication request with username and password) can be forwarded to the attacker's own server*.

Some have brought up the issue of being able to redirect requests to other servers for load balancing, but (while that is not my area of expertise) I would wager that there are better ways to enable load balancing without having to explicitly redirect clients to different hosts.

*please let me know if there any flaws in this line of reasoning. The goal, of course, is not to prevent all attacks, but at least one avenue of attack.

腹黑女流氓 2024-08-27 14:22:20

您应该始终使用完整的 URL。它充当资源的唯一标识符,因为 URL 都必须是唯一的。

我还认为你应该保持一致。由于 Location HTTP 标头需要基于 HTTP 规范的完整 URL,因此当创建新资源时,完整 URL 将在 Location 标头中发送回客户端。如果您在 Location 标头中提供完整的 URL,然后在响应正文中的链接中提供相对 URI,这会很奇怪。

You should always use the full URL. It acts as the unique identifier for the resource since URLs are all required to be unique.

I would also argue that you should be consistent. Since the Location HTTP header expects a full URL based on the HTTP specification, the full URL is sent back in the Location header to the client when a new resource is created. It would be strange for you to provide a full URL in the Location header and then relative URIs in the links within your response body.

花开浅夏 2024-08-27 14:22:20

大型 API 结果中的一个重要考虑因素是重复包含完整 URI 会产生额外的网络开销。不管你相信与否,gzip 并没有完全解决这个问题(不知道为什么)。当结果中包含数百个链接时,完整的 URI 占用了多少空间,我们对此感到震惊。

An important consideration in large API results is the extra network overhead of including the full URI repeatedly. Believe it or not, gzip does not entirely solve this issue (not sure why). We were shocked at how much space the full URI took up when there were hundreds of links included in a result.

只为守护你 2024-08-27 14:22:20

使用绝对 URI 的一个缺点是 api 无法被代理。

收回...不是真的。您应该使用包含域的完整 URL。

One drawback of using absolute URIs is that the api cannot be proxied.

Take it back... not true. You should go for a full URL including the domain.

枕头说它不想醒 2024-08-27 14:22:20

关于优点,我发现传输字节数的减少是以客户端对(绝对)路径所需的额外处理为代价的。如果您迫切希望保存每个字节,即使在尝试将内容编码为 gzip、正确使用缓存标头、使用 etag 和客户端上的条件请求之后,那么这最终可能是必要的,但我期望更高的回报你的努力是其他的。

关于缺点,我认为未来如何引导客户端在资源之间的流动(负载平衡、A/B 测试等)会失去控制,并且我认为这是管理网络的不好做法API。您提供的 URL 对于客户端来说基本上不再是不透明的(请参阅 Tim Berners-Lee Axioms of URI 不透明的 Web 架构)。最后,您有责任让客户对您的 API 的创造性使用感到满意,即使这只是关于您的 URL 空间的结构。如果您需要允许显式定义的 URL 修改,请考虑使用 URI 模板超文本应用程序语言中所使用。

Regarding the pros, I see the reduction in bytes to be transmitted at the expense of extra handling required by a client for the (absolute) path. If you are desperate to save every byte, even after trying content-encoding as gzip, proper use of caching headers, usage of etags and conditional requests on the client, then this may be necessary in the end, but I expect much higher returns on your efforts elsewere.

Regarding the cons, I see a loss of control regarding how you can direct the flow of clients between resources in the future (load balancing, A/B testing, ...), and I would consider it a bad practice regarding managing a web API. The URL you provide is no longer basically opaque for the client (see Tim Berners-Lee Axioms of Web Architecture on URI opacity). In the end, you become responsible to keep clients happy regarding their creative usage of your API, even if it is only regarding the structure of your URL space. Should you ever need to allow for a explicitly defined URL modification, consider the usage of URI templates as used in the Hypertext Application Language.

情归归情 2024-08-27 14:22:20

GET : http://localhost:9090/api/my-service/manage/route/1

UnaryOperator<OutputResponse> addLinkConsumer = (group) -> {
    try {
        return group.add(
                        Link.of(WebMvcLinkBuilder
                                        .linkTo(WebMvcLinkBuilder.methodOn(GroupController.class)
                                                        .getSubById(group.getId()))
                                        .withSelfRel()
                                        .toUri()
                                        .getPath()));
    } catch (Exception e) {
        throw new APIException(DemoRuntimeException.ERROR_IN_LINK_GENERATION,
                        "Failed to generate link for group " + group.getId());
    }
};

HATEOAS 的输出以及 Java Spring Boot 中的相对路径

   {
        "id": 1,
        "name": "some name",
        "links": [
            {
                "rel": "self",
                "href": "/api/my-service/manage/route/1"
            }
        ]
    },

GET : http://localhost:9090/api/my-service/manage/route/1

UnaryOperator<OutputResponse> addLinkConsumer = (group) -> {
    try {
        return group.add(
                        Link.of(WebMvcLinkBuilder
                                        .linkTo(WebMvcLinkBuilder.methodOn(GroupController.class)
                                                        .getSubById(group.getId()))
                                        .withSelfRel()
                                        .toUri()
                                        .getPath()));
    } catch (Exception e) {
        throw new APIException(DemoRuntimeException.ERROR_IN_LINK_GENERATION,
                        "Failed to generate link for group " + group.getId());
    }
};

Output of HATEOAS with relative path in Java spring boot

   {
        "id": 1,
        "name": "some name",
        "links": [
            {
                "rel": "self",
                "href": "/api/my-service/manage/route/1"
            }
        ]
    },
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文