views:

1094

answers:

9

I'm struggling to determine how to design restful URLs. I'm all for the restful approach of using URLs with nouns and not verbs don't understand how to do this.

We are creating a service to implement a financial calculator. The calculator takes a bunch of parameters that we will upload via a CSV file. The use cases would involve:

  1. Upload new parameters
  2. Get the latest parameters
  3. Get parameters for a given business date
  4. Make a set of parameters active
  5. Validate a set of parameters

I gather the restful approach would be to have the following type URLs:

/parameters
/parameters/12-23-2009

You could achieve the first three use cases with:

  1. POST where you include the parameter file in the post request
  2. GET of first URL
  3. GET of second URL

But how do you do the 4th and 5th use case without a verb? Wouldn't you need URLs like:

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

??

A: 

POST /parameters/status?active=false&valid=true

No. The question was: how to do this RESTfully.
Justice
Why is this not restful but the following (from another up voted post) is restful? Only difference seems the data in the query string vs. the body of the request. PUT /parameters/activation HTTP/1.1{ "active": true }
Marcus
Yep that difference is what makes it not RESTful
Breton
To be more specific- The content of the URL should not be responsible for changing the underlying state of the system. It should only be used as an identifier for a particular resource.
Breton
Could somebody tell me which REST constraint this is violating?
Darrel Miller
@Breton Can you point me to the place in RFC2616, or any other spec. for that matter, that specifies this?
Darrel Miller
@Darrel Miller: In terms of restfulness, precisely what resource does the URI identify? What does it mean to POST nothing to that resource, and make that nothing a subordinate to that resource? in terms of the RFC, check section 9.5.
Breton
@Breton As I mentioned in one of the other comments it is valid to POST to a "data handling process". It could also be viewed as "Annotating an existing resource". Although the above url would not be very clear as to its intent, it is not violating any REST constraints that I can see.
Darrel Miller
@Breton Getting people to respect the real REST constraints is hard enough without creating more.
Darrel Miller
A: 

Edit: Indeed the URI would have prevented GET requests from remaining idempotent.


For the validation however, the use of HTTP status codes to notify the validity of a request (to create a new or modify an existing 'parameter') would fit a Restful model.

Report back with a 400 Bad Request status code if the data submitted is/are invalid and the request must be altered before being resubmitted (HTTP/1.1 Status Codes).

This relies on validating at submission time though, rather than deferring it as in your use-case. The other answers have suitable solutions to that scenario.

pyo
The URI is meant to be an identifier. Using a particular URL should not have side effects. Imagine what a proxy would do with that.
Breton
or google, for that matter. I once read a story about a webstore that had all their products deleted by google because of this kind of idiocy.
Breton
+1  A: 

I would suggest the following Meta resource and methods.

Make parameters active and/or validate them:

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

Check if the parameters are active and valid:

> 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': '...'}
< }
<
Andrey Vlasovskikh
Hey, why have you voted down my answer?
Andrey Vlasovskikh
As far as I understand, the question is about the naming of the restful URLs, not about the functionality, isn't it?
poezn
A question confined to "RESTful URLs" is a bad question and should not be answered. The question should instead be expanded to consider "RESTful resources, with associated methods and URLs" - and answered as such.
Justice
As I understood it, the question was about the URL naming conventions **and** the HTTP methods the named resource should respond to.
Andrey Vlasovskikh
+1 to @Justice. Naming conventions is only one part of the story.
Andrey Vlasovskikh
Why -1? At least explain your downvotes.
Andrey Vlasovskikh
+5  A: 

Perhaps something like:

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

{ "active": true }
Justice
Good suggestion. As we could have multiple sets of parameters we would need something like POST /parameters/id/activation ...
Marcus
`POST` is OK if you need to perform a "procedure" like verify the parameters every time you send a request. But when you modify the (application) state of the resource, you actually **update** the existing resource, not create some new resource or post a processing request.
Andrey Vlasovskikh
`POST` is akin to insert and update, while `PUT` is akin to update only.
Justice
@Justice "active" is a property of resource, so it should be `PUT`, not `POST`ed. See the similar "draft" property of the definitely RESTful Atom Publishing Protocol.
Andrey Vlasovskikh
However, you are right in that this arguably should be modelled as a `PUT`.
Justice
Yep, thanks for the feedback. I updated my answer.
Justice
PUT is for creating a new resource, or placing (in whole, not in part) a new resource at a particular URL. I don't see how PUT fits this case.
Breton
..Hrmn.. Maybe I see it now. This could work.
Breton
Actually, `POST` vs `PUT` is not exactly like `insert` vs `update`. `PUT` updates the resource corresponding to the given path, or creates a new resource corresponding to the given path. `POST` creates a new resource somewhere. For example, `PUT /blog/posts/3/comments/5` will update the appropriate comment, while `POST /blog/posts/3/comments` will create a new `comment` resource (and should return the path to the new resource in the response).
Justice
Yeah, I meant to say something like that about PUT, but the words came out slightly ambiguous. Thanks for clarifying.
Breton
@Justice @Breton The more important difference is that `PUT` is idempotent while `POST` is not. Usually you should put as much constraints on what you provide as the result as possible. Sticking with `PUT` gives more information to the client of the service.
Andrey Vlasovskikh
+3  A: 

The design of your urls has nothing to do with whether your application is RESTful or not. the phrase "RESTful URLS" is therefore nonsense.

I think you should do some more reading on what REST actually is. REST treats the URLS as opaque, and as such doesn't know what's in them, whether theres verbs or nouns or whatever. You might still want to design your URLS, but that's about UI, not REST.

That said, lets get to your question: The last two cases are not RESTful, and don't fit into any kind of restful scheme. Those are what you might call RPC. If you're serious about REST you'll have to rethink how your application works from the ground up. Either that, or abandon REST and just do your app as an RPC app.

Hrmmm maybe not.

The idea here is that you have to treat everything as a resource, so once a set of parameters has a URL you can refer to it from, you just add

get [parametersurl]/validationresults

post [paramatersurl]

body: {command:"activate"}

but again, that activate thing is RPC, not REST.

Breton
You state an interesting point here. Can you elaborate a little further how the RESTful approach for something like this would be?
poezn
I've spent a bit of time reading the responses here, and I think justice might be on to something. he models individual properties of your parameters object as individual resources, and uses the PUT verb to replace the contents of that property at that resource. This is modelling the state of each object as a collection of resources, and modifying state as placing or removing or modifying the resource. As for validation- You just need a resource that magically states whether the parameters are valid or not, as above in my answer. That would be fine, as long as that has no side effects.
Breton
Provided of course, that what "Activate" does is merely set a single property to true. If it has to do anything else, then it's still not RESTful, and I'm not sure how you'd model it RESTfully.
Breton
I don't think you can say the last two cases are not RESTful. In effect Activate and Validate are just indirect ways of saying the resource is changing to a new state in a state machine. REST is quite capable of modeling this.
Darrel Miller
+4  A: 

Whenever it looks like you need a new verb, think about turning that verb into a noun instead. For example, turn 'activate' into 'activation', and 'validate' into 'validation'.

But just from what you've written I'd say your application has much bigger problems.

Any time a resource called 'parameter' is proposed, it should send up red flags in every project team member's mind. 'parameter' can literally apply to any resource; it's not specific enough.

What exactly does a 'parameter' represent? Probably a number of different things, each of which should have a separate resource dedicated to it.

Another way to get at this - when you discuss your application with end users (those who presumably know little about programming) what are the words they themselves use repeatedly?

Those are the words you should be designing your application around.

If you haven't yet had this conversion with prospective users - stop everything right now and don't write another line of code until you do! Only then will your team have an idea of what needs to be built.

I know nothing about financial software, but if I had to guess, I'd say some of the resources might go by names such as "Report", "Payment", "Transfer", and "Currency".

There are a number of good books on this part of the software design process. Two I can recommend are Domain Driven Design and Analysis Patterns.

Rich Apodaca
+1 for "what words do the users use?"
jmucchiello
This is a really good point. It's easy to miss if you're in the state of mind for processing formal logic and reasoning. It doesn't matter what X is as long as it fits together with the other parts in a valid way. Human factors just slip away.
Breton
Sometimes I find it useful to convert the words into a "processing resource" like "activator" or "validator". As per RFC 2616 POST can be used to "Provide a block of data...to a data-handling process"
Darrel Miller
Understood. In this case users do refer to the data as "parameters" (or "risk parameters" or something similar). The list of parameters do contain many different types of settings but the parameters are always uploaded as a whole set (in a CSV file).
Marcus
@Marcus - that sounds like a very unusual case. Maybe if you explained what your app does in more detail, we'd be able to offer better suggestions for identifying resources.
Rich Apodaca
A: 

In a REST environment, each URL is a unique resource. What are your resources? A financial calculator really doesn't have any obvious resources. You need to dig into what you are calling parameters and pull out the resources. For example, an amortization calendar for a loan might be a resource. The URL for the calendar might include start_date, term (in months or yers), period (when interest is compounded), interest rate, and initial principle. With all those values you have a specific calendar of payments:

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

Now, I don't know what you are calculating but your concept of a parameter list doesn't sound RESTful. As someone else said, your requirements above sound more XMLRPC. If you are trying for REST you need nouns. Calculations are not nouns, they are verb that act on nouns. You need to turn it around to pull the nouns out of your calcs.

jmucchiello
Breton
You bring up a good point nonetheless. Do these "parameters" even need to be stored serverside? If it's just a one off calculation, why not just make a virtual space, where the parameters are in the URL. As long as you're not changing internal state, it should be fine.
Breton
jmucchiello
SEO does not apply to a web service.
Bob Aman
And "parameters" don't apply to a "resource". A resource is a single entity with a unique identifier. My url identifies a single resource. A parameterized URL indicates a collection of resources you select among using the parameters.
jmucchiello
What sort of links do you think would be on the page indicated by that URL? if I put href="400000", on that page, what do you think would happen? how about href="../6.25/150000", contrast, linking from the amort_cal page to the account_summary page, would I use href="account_summary" or would I have to make it an absolute path? href="/account_summary". forward slashes are significant operators to the browser's relative URL resolver, so it's not a trivial design choice. I assume by the nature of the path segment names, that we *are* talking about a collection of resources to select from.
Breton
I mean, unless you'd actually sit down and create the million permutations that pattern suggests as static html files, we're talking about some kind of script that is taking parameters and generating a resource on the fly. The difference is that the slashes suggest a heirarchical relationship where I don't believe there is one, but the query is quite honest about what's going on, and even names what the different numbers mean, instead of leaving them unlabeled.
Breton
As for SEO, google does fine with query segments. That SEO stuff is an out of date myth.
Breton
Consider the principle of a referentially transparent function, where a call to the function can be replaced with its results without any change to the functioning of the program occurring. Consider how this is similar to a browser requesting a URL from a server, and then requesting the same URL from a local cache, or proxy. The only parameter the cache takes into account is the URL itself, as an opaque string. If you can see this similarity, then you can see that you can have a referentially transparent function as a server process, taking a query string as a parameter and returning a result.
Breton
And that this arrangement would be entirely in keeping with the rest architecture, as the output of this function would be cachable, and each of its results can be referenced as an individual resource.
Breton
"the million permutations that pattern suggests as static html files": Exactly every combination of "parameters" is a different resource as if someone precalculated every possible combination of amortization charts. That's why the URL should look like it points to a unique RESOURCE. We are talking about REST and REST is based on CRUDing RESOURCEs. The 6.25% compounded monthly 30yr fixed mortgage for $200,000 is a different resource than the 5.00% compounded monthly 15yr ARM for $100,000 and therefore has a different URL. I prefer not expressing that through parameters but by name.
jmucchiello
REST doesn't care which you use. that is my point.
Breton
REST is not based on "CRUDing Resources". Sticking all your query parameters into path segments does not automatically make for a RESTful interface because now you think you can call every permutation a resource. Unfortunately there is no magic process that you can apply to identify what the resources in your system should be. It requires careful design, not a mechanical formula.
Darrel Miller
Once again, the REST architecture doesn't CARE what's in the URL. the URL is meant to be *opaque*. It doesn't matter to rest whether you use forward slashes, semicolons, or unicode hearts as seperators. Read this, and respond to this- not to what you imagine me to be saying.
Breton
+13  A: 

General principles for good URI design:

  • 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

(Note: I did not say "RESTful URI design"; URIs are essentially opaque in REST.)

General principles for HTTP method choice:

  • 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

General principles of web service design with HTTP:

  • 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
    • 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 does something like ask for your entire database
    • 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

With regard to your specific question, POST should be used for #4 and #5. These operations fall under the "RPC-like" guideline above. For #5, remember that POST does not necessarily have to use Content-Type: application/x-www-form-urlencoded. This could just as easily be a JSON or CSV payload.

Bob Aman
+1 for "I'm a teapot"
Davide Gualano
@Bob Aman: This list of DO's and DON'T is *excellent* Kudos.
MikeSchinkel
+1  A: 

The activate and validate requirements are situations where you are attempting to change the state of a resource. It is no different that making an order "completed", or some other request "submitted". There are numerous ways to model these kinds of state change but one that I find that often works is to create collection resources for resources of the same state and then to move the resource between the collections to affect the state.

e.g. Create some resources such as,

/ActiveParameters
/ValidatedParameters

If you want to make a set of parameters active, then add that set to the ActiveParameters collection. You could either pass the set of parameters as an entity body, or you could pass an url as a query parameter, as follows:

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

The same thing can be done with the /ValidatedParameters. If the Parameters are not valid then the server can return "Bad Request" to the request to add the parameters to collection of validated parameters.

Darrel Miller