在使用 HATEOAS 设计 RESTful Web 服务时,将链接显示为完整 URL(“ http://server:port/application/customers/1234 ”)与仅显示路径(“/application/客户/1234")?
9 回答
当人们说“相对 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
棘手的是,当省略方案和权限时,“路径”部分本身可以是绝对路径(以 开头/
)或“无根”相对路径。例子:
- 绝对 URI或完整 URI :
"http://example.com:8042/over/there?name=ferret"
- 这是一个相对 uri,带有绝对路径:
/over/there
- 这是一个相对 uri,具有相对路径:
here
或./here
或../here
或等。
因此,如果问题是“服务器是否应该在 RESTful 响应中生成相对路径”,则答案为“否”,详细原因可在此处获得。我认为大多数反对“相对 URI”的人(包括我)实际上都反对“相对路径”。
而在实践中,大多数服务器端MVC框架都可以很容易地生成带有绝对路径的相对URI,例如/absolute/path/to/the/controller
,问题就变成了“服务器实现是否应该scheme://hostname:port
在绝对路径前面加上a前缀”。就像OP的问题一样。我不太确定这个。
一方面,我仍然认为服务器返回一个完整的 uri 是值得推荐的。但是,服务器不应该像这样对源代码中的东西进行硬编码hostname:port
(否则我宁愿回退到具有绝对路径的相对 uri)。解决方案是服务器端始终从 HTTP 请求的“主机”标头中获取该前缀。不确定这是否适用于所有情况。
另一方面,客户端将http://example.com:8042
和 绝对路径连接起来似乎不是很麻烦。毕竟,客户端在向服务器发送请求时已经知道该方案和域名了吧?
总而言之,我会说,建议使用绝对 URI,可能回退到具有绝对路径的相对 URI,永远不要使用相对路径。
这取决于谁在编写客户端代码。如果您正在编写客户端和服务器,那么它并没有太大的区别。您将遭受在客户端或服务器上构建 URL 的痛苦。
但是,如果您正在构建服务器并且希望其他人编写客户端代码,那么如果您提供完整的 URI,他们会更加喜欢您。解析相对 URI 可能有点棘手。首先,如何解决它们取决于返回的媒体类型。HTML 具有基本标记,XML 可以xml:base
在每个嵌套元素中具有标记,Atom 提要可以在提要中具有基,而在内容中可以具有不同的基。如果您不向客户端提供有关基本 URI 的显式信息,那么他们必须从请求 URI 中获取基本 URI,或者可能从Content-Location
标头中获取!并注意尾随斜线。基本 URI 是通过忽略最后一个斜杠右侧的所有字符来确定的。这意味着在解析相对 URI 时,尾部斜杠现在非常重要。
唯一需要提及的另一个问题是文档大小。如果您要返回一个大型项目列表,其中每个项目可能有多个链接,如果您不压缩实体,使用绝对 URL 可能会向您的实体添加大量字节。这是一个性能问题,您需要根据具体情况确定它是否重要。
唯一真正的区别似乎是,如果客户端使用绝对 URI,而不是必须从相对版本构造它们,那么客户端会更容易。当然,这种差异足以动摇我做绝对版本。
随着您的应用程序扩展,您可能希望进行负载平衡、故障转移等。如果您返回绝对 URI,那么您的客户端应用程序将遵循您不断发展的服务器配置。
使用RayLou 的三分法,我的组织选择了支持 (2)。主要原因是为了避免 XSS(跨站点脚本)攻击。问题是,如果攻击者可以将自己的 URL 根注入到从服务器返回的响应中,那么后续的用户请求(例如带有用户名和密码的身份验证请求)可以转发到攻击者自己的服务器*。
有些人提出了能够将请求重定向到其他服务器以进行负载平衡的问题,但是(虽然这不是我的专业领域)我敢打赌,有更好的方法来启用负载平衡,而无需将客户端显式重定向到不同的主机。
*请让我知道这条推理是否有任何缺陷。当然,目标不是阻止所有攻击,而是至少阻止一种攻击途径。
您应该始终使用完整的 URL。它充当资源的唯一标识符,因为 URL 都必须是唯一的。
我还认为你应该保持一致。由于 Location HTTP 标头需要基于 HTTP 规范的完整 URL,因此在创建新资源时,完整 URL 在 Location 标头中发送回客户端。在 Location 标头中提供完整的 URL,然后在响应正文中的链接中提供相对 URI,这会很奇怪。
使用绝对 URI 的一个缺点是无法代理 api。
把它拿回来……不是真的。您应该选择包含域的完整 URL。
大型 API 结果中的一个重要考虑因素是重复包含完整 URI 的额外网络开销。信不信由你,gzip 并没有完全解决这个问题(不知道为什么)。当结果中包含数百个链接时,我们对完整 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.