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

我正在努力决定如何设计restful url。我完全赞成使用带有名词而不是动词的url的restful方法,我不知道如何做到这一点。

我们正在创建一个服务来实现一个金融计算器。计算器接受一堆参数,我们将通过CSV文件上传这些参数。用例包括:

  1. 上传新参数
  2. 获取最新参数
  3. 获取给定业务日期的参数
  4. 激活一组参数
  5. 验证一组参数

我收集的restful方法将有以下类型的url:

/parameters
/parameters/12-23-2009

你可以通过以下方法实现前三个用例:

  1. 在POST请求中包含参数文件
  2. 第一个URL的GET
  3. 第二个URL的GET

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

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

??

98185 次浏览

的确,URI会阻止GET请求保持幂等。


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

如果提交的数据是/是无效的,并且在重新提交请求之前必须修改请求,则报告返回400 Bad Request状态代码(HTTP/1.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': '...'}
< }
<

也许是这样的:

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


{ "active": true }

url的设计与应用程序是否是RESTful无关。因此,“RESTful url”这个短语是毫无意义的。

我认为你应该多读一些关于REST到底是什么的书。REST将url视为不透明的,因此不知道其中有什么,是否有动词或名词或其他什么。您可能仍然希望设计url,但这是关于UI,而不是REST。

也就是说,让我们来回答您的问题:最后两种情况不是RESTful的,不适合任何类型的RESTful方案。这些就是所谓的RPC。如果认真对待REST,就必须重新思考应用程序如何从头开始工作。要么放弃REST,把你的应用程序作为RPC应用程序。

嗯,也许不是。

这里的想法是,你必须把所有东西都当作资源,所以一旦一组参数有了一个URL,你可以引用它,你只需要添加:

GET [parametersurl]/validationresults


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

但是,那个激活的东西是RPC,不是REST。

每当你需要一个新的动词时,考虑把这个动词变成一个名词。例如,将'activate'转换为'activation',将'validate'转换为'validation'。

但是从你写的内容来看,你的应用程序有更大的问题。

任何时候,当一个叫做“参数”的资源被提出时,它都应该在每个项目团队成员的脑海中发出红色信号。'parameter'可以应用于任何资源;还不够具体。

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

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

这些都是你应该围绕着设计你的应用程序的词。

如果您还没有与潜在用户进行这种转换,请立即停止所有工作,在完成转换之前不要再编写另一行代码!只有这样,你的团队才会知道需要构建什么。

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

关于软件设计过程的这一部分,有很多好书。我可以推荐两个领域驱动设计分析模式

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

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

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

好的URI设计的一般原则:

  • 使用查询参数来改变状态
  • 使用混合大小写路径如果你可以帮助它;小写字母最好
  • 在uri中使用特定于实现的扩展(.php, .py, .pl等)。
  • 和你的uri一起落入RPC
  • 尽可能限制你的URI空间
  • 保持路径段短
  • 首选/resource/resource/;创建301重定向从一个你不使用
  • 使用查询参数对资源进行子选择;例如分页,搜索查询
  • 将应该在HTTP报头或正文中的内容移出URI

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

HTTP方法选择的一般原则:

  • 从未使用GET改变状态;这是一个让谷歌机器人毁了你一天的好方法
  • 使用PUT,除非你在更新整个资源
  • 使用PUT,除非你也可以合法地对同一个URI做一个GET
  • 使用POST来检索长期存在的信息或可以合理缓存的信息
  • 使用PUT执行一个不是幂等的操作
  • 使用GET尽可能多
  • 在有疑问时优先使用POST而不是PUT
  • 在你必须做一些类似rpc的事情时使用POST
  • 对较大的或分层的资源类使用PUT
  • 使用DELETE优先于POST来删除资源
  • 使用GET来做类似计算的事情,除非你的输入很大,在这种情况下使用POST

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

  • 将元数据放在响应体中,而响应体应该在头中
  • 将元数据放在一个单独的资源中,除非包含它会产生显著的开销
  • 使用适当的状态码
  • 创建资源后201 Created;必须资源在发送响应时存在
  • 202 Accepted在成功执行操作或异步创建资源后
  • 400 Bad Request当有人对明显是伪造的数据进行操作时;对于您的应用程序,这可能是一个验证错误;通常为未捕获的异常保留500
  • 当有人访问你的API时没有提供必要的Authorization报头,或者当Authorization中的凭据无效时;如果你不希望通过Authorization报头获得凭据,请不要使用此响应代码。
  • 403 Forbidden当有人以一种可能是恶意的方式访问你的API或如果他们没有授权
  • 405 Method Not Allowed当某人使用POST时,他们应该使用PUT,等等
  • 413 Request Entity Too Large当有人试图发送给你一个不可接受的大文件
  • 418 I'm a teapot 当你试图用茶壶煮咖啡时
  • 可以使用缓存头文件
  • 当你可以轻松地将资源减少为哈希值时,ETag头是很好的
  • Last-Modified应该告诉你保持资源更新的时间戳是一个好主意
  • Cache-ControlExpires应该被赋予合理的值
  • 所有你可以在请求中缓存头信息的东西(If-None-ModifiedIf-Modified-Since)
  • 在有意义时使用重定向,但这对于web服务来说应该是罕见的

关于你的具体问题,第4和第5点应该用POST。这些操作属于“rpc -like”;上面的指导方针。对于#5,请记住POST不一定要使用Content-Type: application/x-www-form-urlencoded。这也可以是一个JSON或CSV有效负载。

激活和验证需求是试图更改资源状态的情况。将一个订单“完成”或其他一些请求“提交”是没有区别的。有许多方法可以对这种状态变化建模,但我发现最有效的一种方法是为相同状态的资源创建集合资源,然后在集合之间移动资源以影响状态。

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

/ActiveParameters
/ValidatedParameters

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

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

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

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

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

REST原则上是我们都知道的可浏览Web的泛化,尽管它针对的是各种应用程序,而不仅仅是浏览器。因此,从设计上讲,适用于Web的相同概念也适用于REST体系结构。像这样的问题如何在“休息”中完成某事;方法解决了如何在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>

上面的例子表明,有两个输入字段可以由用户填写,也可以由其他自动机填写,并且在调用submit input元素时,浏览器会将输入数据格式化为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页面进行响应,因为请求表明客户端理解这种格式。

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

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

某些元素的功能可见性,例如在提交输入字段的情况下,通常呈现为按钮,定义了你应该对它做什么。在按钮或链接的情况下,它基本上是告诉你点击它。其他元素可能传达不同的启示。这样的可提供性也可以通过链接关系来表示,即使用带有preload注释的链接,基本上告诉客户端它已经可以在后台加载链接资源的内容,因为用户接下来很可能会获取该内容。这样的链接关系当然应该标准化,或者遵循网络连接定义的关系类型的扩展机制。

这些是在Web上使用的基本概念,也应该在REST体系结构中使用。据“鲍勃叔叔”;Robert C. Martin 架构是关于意图的和REST架构背后的意图是客户端和服务器的解耦,以允许服务器在未来自由发展,而不必担心他们破坏客户端。不幸的是,这需要大量的纪律,因为引入耦合或添加快速修复解决方案来完成工作并继续前进是如此容易。正如Jim Webber在REST架构中指出的那样,作为一个服务提供者,你应该尝试设计一个域应用协议类似于70年代基于文本的电脑游戏,客户端会一直遵循它,直到到达流程的末尾。

多少所谓的“休息”;不幸的是,api在现实中所做的一切都是如此。您可以看到大多数基于JSON的数据的交换是在特定于API的外部文档中指定的,通常很难动态地进行集成。请求的格式也被硬编码到外部文档中,这导致了大量的实现将uri解释为返回预定义的类型,而不是使用一些事先协商好的通用表示格式。这可以防止服务器更改,因为客户端现在希望接收预定义uri的特定数据格式(注意不是表示格式!)。这种自定义的数据格式交换进一步阻止了客户端与其他api交互,因为“数据格式”和“;通常适用于特定的API。我们从过去的RPC技术(如Corba、RMI或SOAP)中了解到这个概念,尽管Peppol最近通过将AS2替换为AS4作为默认传输协议再次转向了它,但我们谴责这些技术在某种程度上是邪恶的。

对于实际的问题,将数据作为csv文件发送与使用application/x-www-form-urlencoded表示或类似的东西没有什么不同。吉姆韦伯明确表示,毕竟HTTP只是一个传输协议,它的应用领域是在Web上传输文档。客户端和服务器端至少都应该支持RFC 7111中定义的text/csv。该CSV文件可以作为处理定义表单元素的媒体类型、发送请求的目标元素或属性以及执行配置上传的HTTP方法的结果而生成。

有几种媒体类型支持表单,如超文本标记语言哈尔形式halform离子九头蛇。不过,我目前还不知道有一种媒体类型可以自动将输入数据直接编码为text/csv,因此可能需要用IANA的媒体类型注册表定义和注册。

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

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

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

源服务器应该验证PUT表示为 与服务器对目标的任何约束一致 PUT不能或不会更改的资源。这是 当源服务器使用内部时尤为重要 与URI相关的配置信息以设置 值用于GET响应上的表示元数据。当一个PUT 表示与目标资源(源)不一致 服务器应该使它们一致,通过转换 表示或更改资源配置,或响应 使用包含足够信息的适当错误消息 解释代表不合适的原因。409(冲突) 或415(不支持的媒体类型)状态码 后者是特定于Content-Type值的约束

例如,如果目标资源被配置为总是有一个 content - text/html的类型;PUT的表示有一个 Content-Type "image/jpeg",源服务器应该做其中之一:

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

< p > b。将PUT表示转换为与之一致的格式 在将该资源保存为新的资源状态之前;或者,< / p > < p > c。使用415(不支持的媒体类型)响应拒绝请求 表示目标资源仅限于"text/html", 可能包括一个链接到不同的资源,将是一个

HTTP没有确切地定义PUT方法如何影响对象的状态 源服务器超越了什么用户可以表达的意图 代理请求和源服务器响应的语义. ...

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