ChatGPT解决这个技术问题 Extra ChatGPT

如何创建没有动词的 REST URL?

我正在努力确定如何设计宁静的 URL。我完全赞成使用带有名词而不是动词的 URL 的宁静方法,我不明白如何做到这一点。

我们正在创建一项服务来实现财务计算器。计算器采用一堆参数,我们将通过 CSV 文件上传这些参数。用例将涉及:

上传新参数 获取最新参数 获取给定业务日期的参数 激活一组参数 验证一组参数

我收集安静的方法是使用以下类型的 URL:

/parameters
/parameters/12-23-2009

您可以通过以下方式实现前三个用例:

POST,您在 post 请求中包含参数文件 第一个 URL 的 GET 第二个 URL 的 GET

但是你如何在没有动词的情况下完成第 4 和第 5 用例呢?你不需要像这样的网址:

/parameters/ID/activate
/parameters/ID/validate

??

我更喜欢 PATCH 而不是 POST 进行部分更新。

C
Community

良好 URI 设计的一般原则:

不要使用查询参数来改变状态

如果可以,请不要使用大小写混合路径;小写最好

不要在 URI 中使用特定于实现的扩展名(.php、.py、.pl 等)

不要用你的 URI 陷入 RPC

尽可能限制你的 URI 空间

保持路径段短

更喜欢 /resource 或 /resource/;从您不使用的重定向创建 301 重定向

使用查询参数对资源进行子选择;即分页,搜索查询

将应该在 HTTP 标头或正文中的内容移出 URI

(注意:我没有说“RESTful URI 设计”;URI 在 REST 中本质上是不透明的。)

HTTP方法选择的一般原则:

永远不要使用 GET 来改变状态;这是让 Googlebot 毁掉你一天的好方法

除非您要更新整个资源,否则不要使用 PUT

不要使用 PUT 除非您也可以合法地在同一个 URI 上执行 GET

不要使用 POST 来检索长期存在的信息或可能合理缓存的信息

不要使用 PUT 执行非幂等操作

尽可能多地使用 GET

如有疑问,请优先使用 POST 而不是 PUT

每当您必须做一些感觉像 RPC 的事情时,请务必使用 POST

对更大或分层的资源类使用 PUT

使用 DELETE 优先于 POST 来删除资源

除非您的输入很大,否则请务必使用 GET 进行计算,在这种情况下使用 POST

使用 HTTP 设计 Web 服务的一般原则:

不要将元数据放在应该在标头中的响应正文中

不要将元数据放在单独的资源中,除非包含它会产生大量开销

请使用适当的状态码

201 创建资源后创建;资源在发送响应时必须存在

202 成功执行操作或异步创建资源后接受

400 Bad Request 当有人对明显伪造的数据进行操作时;对于您的应用程序,这可能是一个验证错误;通常为未捕获的异常保留 500

401 Unauthorized 当有人在没有提供必要的 Authorization 标头或 Authorization 中的凭据无效的情况下访问您的 API 时;如果您不希望通过 Authorization 标头获得凭据,请不要使用此响应代码。

403 Forbidden 当有人以可能是恶意的方式或未经授权访问您的 API 时

405 Method Not Allowed 当有人在应该使用 PUT 时使用 POST 等

413 Request Entity Too Large 当有人试图向您发送一个不可接受的大文件时

418 用茶壶冲泡咖啡时,我是茶壶

尽可能使用缓存标头

当您可以轻松地将资源减少为哈希值时,ETag 标头很好

Last-Modified 应该向您表明,保留资源更新时间的时间戳是个好主意

Cache-Control 和 Expires 应该被赋予合理的值

尽你所能尊重请求中的缓存标头(If-None-Modified、If-Modified-Since)

在有意义的时候使用重定向,但是对于 Web 服务来说这些应该很少见

关于您的具体问题,POST 应该用于#4 和#5。这些操作属于上述“类 RPC”指南。对于 #5,请记住 POST 不一定必须使用 Content-Type: application/x-www-form-urlencoded。这可以很容易地成为 JSON 或 CSV 有效负载。


413 旨在针对您发送的请求的大小,以便您可以礼貌地拒绝某人向您发送数据,通常与 411 一起使用,以便您强迫人们告诉您发送了多少数据。对于针对 413 给出的示例,我认为 400 将是更合适的响应。
+1,因为这是一个很好的资源。但是,它是一种通用资源,并不能直接解决这个问题。理想情况下,这应该包括一个带有特定答案的附加段落。
@GarryShutler 很好,你是绝对正确的。感谢您的编辑。
是的,您只会在覆盖整个对象的情况下使用 PUT。但是,我认为 PATCH 或 POST 在资源的部分更新的情况下是合理的。 PATCH 在操作将要做什么方面更清楚,但由于并非所有客户端都能够发出 PATCH 请求,因此允许 POST 代替是完全合适的,我什至可能会提倡使用如果使用 PATCH,则应始终允许 POST 作为后备。
+1 表示 409 错误。 400 错误可以通过充分的客户端验证来解决。 409 表明请求本身是可接受且一致的,但与服务器状态的某些方面(通常是并发控制,但理论上任何非输入约束)相冲突。
y
yfeldblum

也许是这样的:

PUT /parameters/activation HTTP/1.1
Content-Type: application/json; encoding=UTF-8
Content-Length: 18

{ "active": true }

PUT 用于创建新资源,或将(全部,而不是部分)新资源放置在特定 URL 处。我看不出 PUT 如何适合这种情况。
实际上,POST vs PUT 并不完全像 insert vs updatePUT 更新与给定路径对应的资源,或创建与给定路径对应的新资源。 POST 在某处创建新资源。例如,PUT /blog/posts/3/comments/5 将更新相应的评论,而 POST /blog/posts/3/comments 将创建一个新的 comment 资源(并应在响应中返回新资源的路径)。
@Justice @Breton 更重要的区别是 PUT 是幂等的,而 POST 不是。通常,您应该尽可能多地限制您提供的结果。坚持使用 PUT 可为服务的客户端提供更多信息。
资源也可能是 /parameters/status 并且请求的主体可能只是“活动的”。这样,您就可以以某种方式将全新的资源放置到特定的 URL。
PUT 仅用于(重新)放置整个资源。如果你只传递一个属性,就像你对“活动”所做的那样,你应该使用 PATCH。
R
Rich Apodaca

每当您看起来需要一个新动词时,请考虑将那个动词变成名词。例如,将“激活”变为“激活”,将“验证”变为“验证”。

但就您所写的内容而言,我会说您的应用程序存在更大的问题。

每当提出一种称为“参数”的资源时,它都应该在每个项目团队成员的脑海中发出危险信号。 “参数”可以从字面上适用于任何资源;它不够具体。

“参数”究竟代表什么?可能有很多不同的东西,每一个都应该有一个单独的资源专门用于它。

另一种解决方法 - 当您与最终用户(可能对编程知之甚少的人)讨论您的应用程序时,他们自己重复使用的词是什么?

这些是您应该围绕这些词来设计您的应用程序。

如果您尚未与潜在用户进行此转换 - 立即停止一切,在您完成之前不要编写另一行代码!只有这样,您的团队才会知道需要构建什么。

我对财务软件一无所知,但如果我不得不猜测的话,我会说一些资源可能会以“报告”、“付款”、“转账”和“货币”等名称命名。

关于软件设计过程的这一部分,有很多好书。我可以推荐的两个是 Domain Driven DesignAnalysis Patterns


这是一个很好的观点。如果您处于处理形式逻辑和推理的心态,很容易错过。 X 是什么并不重要,只要它以有效的方式与其他部分结合在一起即可。人为因素就这么溜走了。
有时我发现将单词转换为“处理资源”如“激活器”或“验证器”很有用。根据 RFC 2616 POST 可用于“向数据处理过程提供数据块……”
明白了。在这种情况下,用户确实将数据称为“参数”(或“风险参数”或类似的东西)。参数列表确实包含许多不同类型的设置,但参数总是作为一个整体上传(在 CSV 文件中)。
@Marcus - 这听起来像是一个非常不寻常的案例。也许如果您更详细地解释您的应用程序的功能,我们将能够为识别资源提供更好的建议。
“当您与最终用户讨论您的应用程序时,他们自己重复使用的词是什么?” ...如果它们都是动词呢? XD
M
MarredCheese

URL 的设计与您的应用程序是否为 RESTful 无关。因此,短语“RESTful URLs”是无稽之谈。

我认为您应该更多地阅读 REST 实际上是什么。 REST 将 URL 视为不透明的,因此不知道其中有什么,是否有动词或名词等等。您可能仍想设计您的 URL,但那是关于 UI,而不是 REST。

也就是说,让我们来回答您的问题:最后两种情况不是 RESTful 并且不适合任何类型的 RESTful 方案。这些就是你可能称之为 RPC 的东西。如果您对 REST 很认真,您将不得不重新考虑您的应用程序是如何从头开始工作的。要么放弃 REST,要么将您的应用程序作为 RPC 应用程序。

嗯,也许不是。

这里的想法是您必须将所有内容视为资源,因此一旦一组参数有一个您可以引用它的 URL,您只需添加:

GET [parametersurl]/validationresults

POST [paramatersurl]
body: {command:"activate"}

但同样,激活的东西是 RPC,而不是 REST。


你在这里陈述了一个有趣的观点。您能否进一步详细说明此类事情的 RESTful 方法是怎样的?
我花了一些时间阅读这里的回复,我认为正义可能会有所作为。他将参数对象的各个属性建模为各个资源,并使用 PUT 动词替换该资源中该属性的内容。这将每个对象的状态建模为资源的集合,并将状态修改为放置或移除或修改资源。至于验证 - 您只需要一个能神奇地说明参数是否有效的资源,如我的回答中所述。没问题,只要没有副作用。
当然,前提是“Activate”所做的只是将单个属性设置为 true。如果它必须做任何其他事情,那么它仍然不是 RESTful,而且我不确定你将如何对它进行 RESTful 建模。
我认为你不能说最后两种情况不是 RESTful。实际上,激活和验证只是表示资源正在状态机中更改为新状态的间接方式。 REST 非常有能力对此进行建模。
@Darrel,我认为您指出了 REST 的一部分,这对于许多刚接触 REST 的人来说可能具有挑战性。您将如何实施“验证资源 x”操作?我认为具有挑战性的事情是它是一个可能导致状态变化的操作,但新状态是请求的结果。
D
Darrel Miller

激活和验证要求是您尝试更改资源状态的情况。将订单“完成”或其他请求“提交”没有什么不同。有很多方法可以对这些状态变化进行建模,但我发现一种通常有效的方法是为相同状态的资源创建集合资源,然后在集合之间移动资源以影响状态。

例如创建一些资源,例如,

/ActiveParameters
/ValidatedParameters

如果要使一组参数处于活动状态,则将该组添加到 ActiveParameters 集合中。您可以将参数集作为实体主体传递,也可以将 url 作为查询参数传递,如下所示:

POST /ActiveParameters?parameter=/Parameters/{Id}

使用 /ValidatedParameters 可以完成相同的操作。如果参数无效,则服务器可以向请求返回“错误请求”,以将参数添加到已验证参数的集合中。


A
Andrey Vlasovskikh

我会建议以下 Meta 资源和方法。

激活参数和/或验证它们:

> PUT /parameters/<id>/meta HTTP/1.1
> Host: example.com
> Content-Type: application/json
> Connection: close
>
> {'active': true, 'require-valid': true}
>
< HTTP/1.1 200 OK
< Connection: close
<

检查参数是否有效:

> GET /parameters/<id>/meta HTTP/1.1
> Host: example.com
> Connection: close
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Connection: close
<
< {
<     'active': true,
<     'require-valid': true,
<     'valid': {'status': false, 'reason': '...'}
< }
<

据我了解,问题是关于静止 URL 的命名,而不是关于功能,不是吗?
仅限于“RESTful URL”的问题是一个不好的问题,不应该回答。应该将问题扩展为考虑“带有关联方法和 URL 的 RESTful 资源” - 并就此进行回答。
据我了解,问题是关于 URL 命名约定和命名资源应响应的 HTTP 方法。
C
Community

看到 10 多年后没有答案真正说明如何在 REST 架构中设计 OP 中要求的这种东西,我感到有点难过,因此我觉得现在有必要这样做。

首先,什么是 REST?!首字母缩略词 REST 或 ReST 代表“Representational State Transfer”,并以某种表示格式定义资源状态的交换。表示格式趋向于协商的媒体类型。在 application/html 的情况下,表示格式可能是在浏览器中呈现的 HTML 格式文本内容流,可能是在应用一些样式表格式以将某些元素定位在某些位置之后。

REST 原则上是我们都知道的可浏览 Web 的概括,尽管它针对各种应用程序而不仅仅是浏览器。因此,通过设计,适用于 Web 的相同概念也适用于 REST 架构。诸如如何以“RESTful”方式实现某事的问题围绕回答如何在 Web 页面上实现某事然后将相同的概念应用到应用程序层的问题来解决。

基于 Web 的计算器通常从一些“页面”开始,允许您在将输入的数据发送到服务器之前输入一些值进行计算。在 HTML 中,这通常是通过 HTML <form> 元素实现的,该元素向客户端介绍要设置的可用参数、将请求发送到的目标位置以及在发送输入数据时应用的表示格式。这可以看起来像这样:

<html>
  <head>
    ...
  </head>
  <body>
    <form action="/../someResource" method="post" enctype="application/x-www-form-urlencoded">
      <label for="firstNumber">First number:</label>
      <input type="number" id="firstNumber" name="firstNumber"/>

      <label for="secondNumber">Second number:</label>
      <input type="number" id="secondNumber" name="secondNumber"/>

      <input type="submit" value="Add numbers"/>
    </form>
  </body>
</html>

上面的示例 ie 声明有两个输入字段可以由用户或其他自动机填写,并且在调用提交输入元素时,浏览器负责将输入数据格式化为 application/x-www-form-urlencoded 表示格式通过指定的 HTTP 请求方法(在本例中为 POST)发送到提到的目标位置。如果我们在 firstNumber 输入字段中输入 1 并在 secondNumber 输入字段中输入 2,浏览器将生成 firstNumber=1&secondNumber=2 的表示并将其作为实际请求的正文负载发送到目标资源.

因此,向服务器发出的原始 HTTP 请求可能如下所示:

POST /../someResource
Host: www.acme.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Accept: application/html

firstNumber=1&secondNumber=2

服务器可以执行计算并使用包含计算结果的另一个 HTML 页面进行响应,因为请求表明客户端理解这种格式。

正如 Breton 已经指出的那样,没有“RESTful” URL 或 URI 这样的东西。 URI/URL 是它自己的东西,不应该向客户端/用户传达任何含义。在上面的计算器示例中,用户根本不感兴趣将数据发送到哪里,只对触发提交输入字段时发送请求感兴趣。服务器应该已经提供了执行任务所需的所有必需信息。

浏览器也可能不知道该请求实际上是在为计算器提供一些输入参数,它也可能是某种订单表单,它只返回下一个表单表示以继续订购过程或某种完全不同的类型资源。在这种情况下,它只是执行 HTML 规范要求的内容,它并不关心服务器实际在做什么。这个概念使浏览器能够使用相同的表示格式来执行各种操作,例如从您喜欢的在线商店订购一些东西、与您最好的朋友聊天、登录在线帐户等等。

某些元素的 affordance(例如在通常呈现为按钮的提交输入字段案例中)定义了您应该使用它做什么。对于按钮或链接,它基本上会告诉您单击它。其他元素可能传达不同的启示。这种可供性也可以通过 link-relations 表示,即用 preload 注释链接基本上告诉客户端它已经可以在后台加载链接资源的内容,因为用户很可能接下来会抓取该内容。这样的链接关系当然应该标准化或遵循Web linking定义的关系类型的扩展机制。

这些是在 Web 上使用的基本概念,也应该在 REST 架构中使用。根据“Uncle Bob”Robert C. Martin an architecture is about intent 的说法,REST 架构背后的意图是将客户端与服务器分离,以允许服务器在未来自由发展,而不必担心它们会破坏客户端。不幸的是,这需要大量的纪律,因为很容易引入耦合或添加快速修复解决方案来完成工作并继续前进。正如 Jim Webber 在 REST 架构中指出的那样,作为服务提供者,您应该尝试设计一个 domain application protocol similar to a text based computer game of the 70s,客户端将一直执行到流程结束为止。

不幸的是,许多所谓的“REST”API 在现实中所做的一切都是如此。您会看到在 API 特定的外部文档中指定的主要基于 JSON 的数据的交换,通常很难动态地动态集成。请求所需的格式也被硬编码到外部文档中,这导致大量实现将 URI 解释为 return predefined typs,而不是使用一些预先协商的通用表示格式。这可以防止服务器发生变化,因为客户端现在期望接收预定义 URI 的某种数据格式(注意不是表示格式!)。这种自定义数据格式交换进一步阻止了客户端与其他 API 交互,因为“数据格式”通常是针对特定 API 的。我们从过去的 RPC 技术(例如 Corba、RMI 或 SOAP)中知道这个概念,我们谴责它们在某种程度上是邪恶的,尽管 Peppol 最近再次使用它,将 AS2 替换为 AS4 作为默认传输协议。

关于提出的实际问题,将数据作为 csv 文件发送与使用 application/x-www-form-urlencoded 表示或类似的东西没有什么不同。 Jim Webber 明确表示,毕竟HTTP is just a transport protocol whose application domain is the transfer of documents over the Web。客户端和服务器至少都应该支持 RFC 7111 中定义的 text/csv。这个 CSV 文件可以作为处理媒体类型的结果生成,该媒体类型定义了表单元素、将请求发送到的目标元素或属性以及执行配置上传的 HTTP 方法。

有几种媒体类型支持 HTMLHAL FormshalformionHydra 等形式。不过,我目前不知道有一种媒体类型可以自动将输入数据直接编码为 text/csv,因此可能需要在 IANA's media type registry 中定义和注册。

我猜完整参数集的上传和下载应该不是问题。如前所述,目标 URI 不相关,因为客户端只会使用 URI 来检索要处理的新内容。按营业日期过滤也应该不难。然而,这里服务器应该为客户端提供客户端可以选择的所有可能性。近年来,GraphQL 和 RestQL 不断发展,它们引入了一种类似 SQL 的语言,可以针对某个端点来获得过滤的响应。然而,在真正的 REST 意义上,这违反了 REST 背后的理念,因为 a) GraphQL 即仅使用单个端点,这会以某种方式阻止缓存的最佳使用 b) 需要了解可用字段,这可能会导致引入客户端耦合到资源的基本数据模型。

激活或停用某些配置参数只需触发提供此功能的超媒体控件即可。在 HTML 表单中,这可能是一个简单的复选框或列表中的多行选择或类似的。根据表单和它定义的方法,它可能会通过 PUT 发送整个配置,或者对所做的更改很聪明,只通过 PATCH 执行部分更新。后者基本上需要计算对更新表示的更改表示,并为服务器提供所需的步骤以将当前表示转换为所需的表示。根据 PATH specification,这必须在事务中完成,以便应用所有步骤或不应用任何步骤。

HTTP 允许并鼓励服务器在应用更改之前预先验证收到的请求。对于 PUT,规范规定:

源服务器应该验证 PUT 表示是否与服务器对目标资源的任何约束一致,这些约束不能或不会被 PUT 更改。当源服务器使用与 URI 相关的内部配置信息来设置 GET 响应中表示元数据的值时,这一点尤其重要。当 PUT 表示与目标资源不一致时,源服务器应该通过转换表示或更改资源配置使它们保持一致,或者以包含足够信息的适当错误消息进行响应,以解释表示不合适的原因。建议使用 409(冲突)或 415(不支持的媒体类型)状态代码,后者特定于对 Content-Type 值的约束。例如,如果目标资源配置为始终具有“text/html”的 Content-Type 并且被 PUT 的表示具有“image/jpeg”的 Content-Type,则源服务器应该执行以下操作之一:重新配置目标资源以反映新的媒体类型;湾。将 PUT 表示转换为与资源一致的格式,然后将其保存为新的资源状态;或者,c。使用 415(不支持的媒体类型)响应拒绝请求,指示目标资源仅限于“text/html”,可能包括指向不同资源的链接,该链接将成为新表示的合适目标。 HTTP 没有准确定义 PUT 方法如何影响源服务器的状态,超出了用户代理请求的意图和源服务器响应的语义所能表达的范围。 ...

总结这篇文章,您应该使用现有的媒体类型,它允许您向客户端介绍所需或支持的输入参数、将请求发送到的目标位置、要使用的操作以及媒体类型请求必须格式化,或者定义您自己在 IANA 注册的请求。如果您想将输入转换为 text/csv,然后将 CSV 表示上传到服务器,则后者可能是必需的。验证应在更改应用于资源之前进行。实际的 URI 不应该与客户端相关,除了确定将请求发送到哪里,因此可以由您(服务实现者)自由选择。通过执行这些步骤,您几乎可以获得随时更改服务器端的自由,并且如果客户端支持使用的媒体类型,则客户端不会因此而中断。


D
Derek Mortimer

编辑: 实际上,URI 会阻止 GET 请求保持幂等性。

然而,对于验证,使用 HTTP 状态代码来通知请求的有效性(创建新的或修改现有的“参数”)将适合 Restful 模型。

如果提交的数据无效并且必须在重新提交之前更改请求 (HTTP/1.1 Status Codes),请使用 400 Bad Request 状态代码进行报告。

不过,这依赖于在提交时进行验证,而不是像在您的用例中那样推迟它。其他答案对这种情况有合适的解决方案。


URI 是一个标识符。使用特定的 URL 不应该有副作用。想象一下代理会用它做什么。
或谷歌,就此而言。我曾经读过一个关于一家网上商店的故事,因为这种白痴行为,他们的所有产品都被谷歌删除了。
M
MarredCheese

在 REST 环境中,每个 URL 都是唯一的资源。你的资源是什么?财务计算器确实没有任何明显的资源。您需要深入了解您正在调用的参数并提取资源。例如,贷款的摊销日历可能是一种资源。日历的 URL 可能包括开始日期、期限(以月或年为单位)、期间(复利时)、利率和初始本金。有了所有这些值,您就有了一个特定的付款日历:

http://example.com/amort_cal/2009-10-20/30yrsfixed/monthly/5.00/200000

现在,我不知道您在计算什么,但是您的参数列表概念听起来不像 RESTful。正如其他人所说,您的上述要求听起来更像是 XMLRPC。如果您尝试使用 REST,则需要名词。计算不是名词,它们是作用于名词的动词。你需要把它转过来把名词从你的计算中拉出来。


我认为在这里使用正斜杠有点傻, amort_cal?date=2009-10-20&type=30yrsfixed&period=monthly&rate=5.0&initialamount=200000 有什么问题? REST 不在乎,只要它是一种资源。不过,URI 规范确实很关心。你如何想象相对链接与这样的方案一起使用?
尽管如此,你还是提出了一个很好的观点。这些“参数”甚至需要存储在服务器端吗?如果只是一次性计算,为什么不做一个虚拟空间,参数在 URL 中。只要你不改变内部状态,它应该没问题。
并且“参数”不适用于“资源”。资源是具有唯一标识符的单个实体。我的 url 标识一个资源。参数化 URL 表示您使用参数在其中选择的资源集合。
REST 不基于“CRUDing Resources”。将所有查询参数粘贴到路径段中并不会自动生成 RESTful 接口,因为现在您认为可以将每个排列称为资源。不幸的是,没有可以应用的神奇过程来确定系统中的资源应该是什么。它需要精心设计,而不是机械公式。
再一次,REST 架构不关心 URL 中的内容。 URL 是不透明的。无论您使用正斜杠、分号还是 unicode 心作为分隔符,休息都没有关系。阅读这篇文章,并对此做出回应——而不是你想象中的我在说什么。