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

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

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

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

/parameters
/parameters/12-23-2009

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

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

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

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

??


当前回答

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

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

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

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

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

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

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

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

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

其他回答

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

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

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

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

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

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

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

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

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

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


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

如果提交的数据无效,并且在重新提交请求之前必须修改请求,则返回400坏请求状态码(HTTP/1.1状态码)。

不过,这依赖于在提交时进行验证,而不是像在用例中那样推迟。其他答案都有适合该场景的解决方案。

好的URI设计的一般原则:

Don't use query parameters to alter state Don't use mixed-case paths if you can help it; lowercase is best Don't use implementation-specific extensions in your URIs (.php, .py, .pl, etc.) Don't fall into RPC with your URIs Do limit your URI space as much as possible Do keep path segments short Do prefer either /resource or /resource/; create 301 redirects from the one you don't use Do use query parameters for sub-selection of a resource; i.e. pagination, search queries Do move stuff out of the URI that should be in an HTTP header or a body

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

HTTP方法选择的一般原则:

Don't ever use GET to alter state; this is a great way to have the Googlebot ruin your day Don't use PUT unless you are updating an entire resource Don't use PUT unless you can also legitimately do a GET on the same URI Don't use POST to retrieve information that is long-lived or that might be reasonable to cache Don't perform an operation that is not idempotent with PUT Do use GET for as much as possible Do use POST in preference to PUT when in doubt Do use POST whenever you have to do something that feels RPC-like Do use PUT for classes of resources that are larger or hierarchical Do use DELETE in preference to POST to remove resources Do use GET for things like calculations, unless your input is large, in which case use POST

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

Don't put metadata in the body of a response that should be in a header Don't put metadata in a separate resource unless including it would create significant overhead Do use the appropriate status code 201 Created after creating a resource; resource must exist at the time the response is sent 202 Accepted after performing an operation successfully or creating a resource asynchronously 400 Bad Request when someone does an operation on data that's clearly bogus; for your application this could be a validation error; generally reserve 500 for uncaught exceptions 401 Unauthorized when someone accesses your API either without supplying a necessary Authorization header or when the credentials within the Authorization are invalid; don't use this response code if you aren't expecting credentials via an Authorization header. 403 Forbidden when someone accesses your API in a way that might be malicious or if they aren't authorized 405 Method Not Allowed when someone uses POST when they should have used PUT, etc 413 Request Entity Too Large when someone attempts to send you an unacceptably large file 418 I'm a teapot when attempting to brew coffee with a teapot Do use caching headers whenever you can ETag headers are good when you can easily reduce a resource to a hash value Last-Modified should indicate to you that keeping around a timestamp of when resources are updated is a good idea Cache-Control and Expires should be given sensible values Do everything you can to honor caching headers in a request (If-None-Modified, If-Modified-Since) Do use redirects when they make sense, but these should be rare for a web service

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

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

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

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

/ActiveParameters
/ValidatedParameters

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

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

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