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

时间:2009-10-24 21:14:57

标签: rest restful-url

我很难确定如何设计restful URL。我完全是为了使用带名词的URL而不是动词,这是不明白如何做到的。

我们正在创建一项服务来实施金融计算器。计算器需要一堆参数,我们将通过CSV文件上传。用例包括:

  1. 上传新参数
  2. 获取最新参数
  3. 获取指定营业日期的参数
  4. 激活一组参数
  5. 验证一组参数
  6. 我收集的其余方法是拥有以下类型的网址:

    /parameters
    /parameters/12-23-2009
    

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

    1. 在post请求中包含参数文件的POST
    2. 获取第一个网址
    3. 获取第二个网址
    4. 但是你怎么做没有动词的第4和第5个用例?您不需要以下网址:

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

      ...

9 个答案:

答案 0 :(得分:981)

良好URI设计的一般原则:

  • 使用查询参数来改变状态
  • 如果可以提供帮助,请使用混合大小写路径;小写是最好的
  • 不要在您的URI(.php,.py,.pl等)中使用特定于实现的扩展程序
  • 使用您的URI
  • 落入RPC
  • 尽可能限制您的URI空间
  • 保持路径段短
  • 更喜欢/resource/resource/;从您不使用的
  • 创建301重定向
  • 执行使用查询参数进行资源的子选择;即分页,搜索查询
  • 执行移出应该位于HTTP标头或正文中的URI

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

HTTP方法选择的一般原则:

  • 不要曾使用GET来改变状态;这是让Googlebot破坏你的一天的好方法
  • 除非您要更新整个资源,否则使用PUT
  • 不要使用PUT,除非你也可以在同一个URI上合法地进行GET
  • 使用POST检索长期存在或可能合理缓存的信息
  • 使用PUT执行非idempotent的操作
  • 尽可能使用GET
  • 时,优先使用POST而不是PUT
  • 只要您需要做类似RPC的事情
  • ,就使用POST
  • 执行将PUT用于较大或分层的资源类
  • 执行优先使用DELETE删除资源
  • 使用GET进行计算等操作,除非输入很大,在这种情况下使用POST

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

  • 不要将元数据放在应该位于标题中的响应正文中
  • 不要将元数据放在单独的资源中,除非包含它会产生大量开销
  • 执行使用适当的状态代码
      创建资源后
    • 201 Created;资源必须在发送回复时存在
    • 成功执行操作或异步创建资源后
    • 202 Accepted
    • 400 Bad Request当某人对明显虚假的数据进行操作时;对于您的应用程序,这可能是一个验证错误;通常为未捕获的例外保留500
    • 401 Unauthorized当有人访问您的API时,如果没有提供必要的Authorization标头,或Authorization内的凭据无效;如果您不希望通过Authorization标题获取凭据,请不要使用此响应代码。
    • 403 Forbidden当有人以恶意或未经授权的方式访问您的API时
    • 405 Method Not Allowed当有人使用POST时应该使用PUT等
    • 413 Request Entity Too Large当有人试图向您发送不可接受的大文件时
    • 418 I'm a teapot when attempting to brew coffee with a teapot
  • 尽可能使用缓存标头
      当您可以轻松地将资源减少为哈希值时,
    • ETag标题很好
    • Last-Modified应该向您表明,保持资源更新时间的时间戳是个好主意
    • Cache-ControlExpires应该给出明智的价值
  • 执行您可以在请求中使用缓存标头的所有内容(If-None-ModifiedIf-Modified-Since
  • 在有意义的情况下使用重定向,但这对于网络服务来说应该是罕见的

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

答案 1 :(得分:71)

也许是这样的:

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

{ "active": true }

答案 2 :(得分:18)

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

但是,根据你所写的内容,我会说你的应用程序有更大的问题。

每当提出一个名为'parameter'的资源时,它应该在每个项目团队成员的心中发出红旗。 '参数'可以逐字地应用于任何资源;它不够具体。

'参数'究竟代表什么?可能有许多不同的东西,每个东西都应该有一个专门的资源。

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

这些是您应该设计应用程序的词语。

如果您尚未与潜在用户进行此转换 - 请立即停止所有操作,并且在您执行此操作之前不要编写另一行代码!只有这样,您的团队才能了解需要构建的内容。

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

软件设计过程的这一部分有很多好书。我建议的两个是Domain Driven DesignAnalysis Patterns

答案 3 :(得分:11)

您的网址设计与您的应用程序是否为REST是无关的。因此,短语“RESTful URLS”是无意义的。

我认为你应该更多地了解REST究竟是什么。 REST将URL视为不透明,因此不知道它们中的内容,无论是动词还是名词或其他什么。您可能仍想设计URL,但这是关于UI而不是REST。

那就是说,让我们回答你的问题:最后两个案例不是RESTful,并且不适合任何类型的宁静方案。这些就是你所谓的RPC。如果您对REST非常认真,那么您必须重新思考应用程序的工作原理。要么是这样,要么放弃REST,只是将你的应用作为RPC应用程序。

Hrmmm也许不是。

这里的想法是你必须将所有东西视为一种资源,所以一旦一组参数有一个你可以引用它的URL,你只需添加

获取[parametersurl] / validationresults

post [paramatersurl]

body:{command:“activate”}

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

答案 4 :(得分:6)

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

e.g。创建一些资源,例如,

/ActiveParameters
/ValidatedParameters

如果要激活一组参数,请将该组添加到ActiveParameters集合中。您可以将参数集作为实体主体传递,也可以将URL作为查询参数传递,如下所示:

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

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

答案 5 :(得分:1)

我建议使用以下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': '...'}
< }
<

答案 6 :(得分:1)

我觉得有点悲哀地看到,经过10年以上没有答案真的,说明如何作为OP要求这样的事情可能会在REST架构来设计的,因此我觉得有必要现在就这样做。< / p>

首先,什么是REST ?!缩写REST或ReST代表“表示状态转移”,并以某种表示格式定义资源状态的交换。表示格式符合协商的媒体类型。对于application/html,表示格式可能是在浏览器中呈现的HTML格式的文本内容流,可能是在应用了某些样式表格式以将某些元素放置在某些位置之后。

REST原则上是众所周知的可浏览Web的概括,尽管它针对的是各种应用程序,而不仅针对浏览器。因此,通过设计,适用于Web的相同概念也适用于REST体系结构。诸如如何以“ RESTful”方式实现某些目标之类的问题解决了围绕以下问题的答案:如何在网页上实现某些目标,然后将相同的概念应用于应用程序层。

基于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>

上面的示例即指出,有两个输入字段可以由用户或其他一些自动机填写,并且在调用Submit输入元素时,浏览器会负责将输入数据格式化为{{ 1}}表示形式,通过指定的HTTP请求方法(在这种情况下为application/x-www-form-urlencoded)发送到上述目标位置。如果我们在POST输入字段中输入1,在firstNumber输入字段中输入2,浏览器将生成secondNumber的表示并将其作为对目标资源的实际请求的主体有效负载。

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

firstNumber=1&secondNumber=2

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

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

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

某些元素的affordance,例如在通常以按钮形式呈现的“提交输入”字段中,定义了应该使用的元素。对于按钮或链接,它基本上会告诉您单击它。其他元素可以传达不同的能力。也可以通过link-relations来表示这种可承受性,即使用POST /../someResource Host: www.acme.com Content-Type: application/x-www-form-urlencoded Content-Length: 28 Accept: application/html firstNumber=1&secondNumber=2 带注释的链接,该链接基本上告诉客户端它已经可以在后台加载链接资源的内容,因为用户很可能会抓住它。接下来的内容。当然,此类链接关系应标准化或遵循Web linking所定义的关系类型扩展机制。

这些是在Web上使用的基本概念,也应该在REST体系结构中使用。根据“鲍勃叔叔” 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,客户将遵循这些return predefined typs,直到他们到达流程结束为止。

不幸的是,现实中有很多所谓的“ REST” API只是在做这些。您会看到在特定于API的外部文档中指定的大多数基于JSON的数据交换,这些文档通常很难动态集成。请求的外观格式也被硬编码到外部文档中,这导致大量实现将URI解释为HTTP is just a transport protocol whose application domain is the transfer of documents over the Web,而不是使用预先协商的常见表示格式。这可以防止服务器发生更改,因为客户端现在期望接收预定义URI的某种数据格式(注意,不是表示格式!)。这种自定义数据格式交换还可以防止客户端与其他API进行交互,因为“数据格式”通常是特定API的潮流。我们从过去的RPC技术(例如Corba,RMI或SOAP)中就知道了这一概念,尽管Peppol最近通过将AS2替换为AS4作为默认传输协议而再次使用它,但我们还是谴责这种概念。

关于所问的实际问题,将数据作为csv文件发送与使用preload表示形式或类似内容没什么不同。吉姆·韦伯(Jim Webber)明确指出,毕竟RFC 7111。客户端和服务器至少应同时支持HTML中定义的application/x-www-form-urlencoded。可以通过处理定义了表单元素的媒体类型,将请求发送到的目标元素或属性以及执行配置上载的HTTP方法来生成此CSV文件。

有几种支持格式的媒体类型,例如HAL FormshalformionHydraIANA's media type registry。不过,我目前尚不了解一种可以自动将输入数据直接编码为text/csv的媒体类型,因此可能需要定义一个媒体类型并将其注册到PATH specification

我猜,完整参数集的上传和下载应该不是问题。如前所述,目标URI不相关,因为客户端将仅使用URI检索要处理的新内容。按营业日期过滤也不难。但是,服务器在此应具有客户端可以选择的所有可能性的客户端。近年来,GraphQL和RestQL不断发展,它们引入了类似SQL的语言,可以将其定位于某个端点以获得经过过滤的响应。但是,从真正的REST的角度来看,这违反了REST的思想,因为a)GraphQL,即仅使用单个端点,从而以某种方式阻止了缓存的最佳使用,并且b)要求了解可用字段,这可能导致引入客户端耦合资源的基本数据模型。

激活或取消激活某些配置参数仅是触发提供此功能的超媒体控件。在HTML表单中,这可以是简单的复选框,也可以是列表中的多行选择,也可以是那种。然后,根据其形式和定义的方法,它可能会通过text/csv发送整个配置,或者对所做的更改很精明,仅通过PUT执行部分更新。后者基本上需要对更新表示的变更表示进行计算,并向服务器提供所需的步骤,以将当前表示转换为所需的表示。根据{{​​3}},此操作必须在事务内完成,以便应用所有步骤或不应用任何步骤。

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

原始服务器应验证PUT表示是否为 与服务器对目标的任何约束一致 PUT无法或不会更改的资源。这是 当原始服务器使用内部时特别重要 与URI相关的配置信息,以便设置 GET响应上表示元数据的值。当一个PUT 表示与目标资源,来源不一致 服务器应通过转换 表示或更改资源配置,或响应 包含适当信息的适当错误消息 解释为什么这种表示方式不合适。 409(冲突) 建议使用或415(不受支持的媒体类型)状态代码, 后者专门针对Content-Type值的约束。

例如,如果目标资源配置为始终具有一个 Content-Type为“ text / html”,表示形式为PUT,具有 Content-Type为“ image / jpeg”,原始服务器应执行以下操作之一:

a。重新配置目标资源以反映新的媒体类型;

b。将PUT表示转换为与之一致的格式 将资源保存为新资源状态之前;或者,

c。拒绝请求并返回415(不支持的媒体类型)响应 指示目标资源仅限于“ text / html”, 也许包括指向其他​​资源的链接,这将是 新展示形式的合适目标。

HTTP并未确切定义PUT方法如何影响状态的 源服务器超出了用户的意图所能表达的范围 代理请求和原始服务器响应的语义。 ...

总结一下,您应该使用现有的媒体类型,该媒体类型可以让您了解所需的或受支持的输入参数,向其发送请求的目标位置,要使用的操作以及媒体的客户端-类型,该请求必须采用格式,或者定义您自己的请求,并向IANA注册。如果要将输入转换为PATCH,然后将CSV表示形式上载到服务器,则可能需要后者。验证应在将更改应用于资源之前进行。实际的URI不应与客户端相关,而应确定将请求发送至何处,因此服务提供者可以自由选择。通过执行以下步骤,您几乎可以随时更改服务器端,并且如果客户端支持使用的媒体类型,客户端也不会因此而中断。

答案 7 :(得分:0)

编辑:确实,URI会阻止GET个请求保持幂等。


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

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

这依赖于在提交时验证,而不是像在用例中那样推迟验证。其他答案适用于该场景。

答案 8 :(得分:0)

在REST环境中,每个URL都是唯一的资源。你有什么资源?金融计算器确实没有任何明显的资源。您需要深入研究您所调用的参数并提取资源。例如,贷款的摊还日历可能是资源。日历的URL可能包括start_date,期限(以月或年为单位),期间(当利息复利时),利率和初始原则。对于所有这些值,您有特定的付款日历:

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

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