REST "create if not exists"
"francoisverry" <[email protected]>
| Newsgroups | gmane.comp.web.services.rest |
|---|---|
| Message-ID | <[email protected]> |
Hello
I'm new to REST, and I'm trying to integrate it in my company. We
currently are trying to design a multi-criteria search engine with REST
principles. I use as a reference the O'Reilly "RESTful Web Services".
I don't know if the following problem has been discussed somewhere, but
I've been unable to find anything that matches it. If you happen to know
resources that could help me, please let me know.
In a multi-critera search engine, we have two types of resources :
"Results" and "Filters". Results are items searched for, and Filters are
sets of criteria which allows us to isolate a subset of Results.
For example :
GET /results returns the total number of results
GET /results/{result-id} returns one specific result
GET /filters returns a specification on criteria usage (eg. an empty
<form>)
GET /filters/{filter-id} returns one specific filter (eg. a filled
<form>)
GET /filters/{filter-id}/results returns the total number of results
matching the filter, and links to the corresponding /results/{result-id}
We want to allow the registration of filters. Imagine a filter as the
composition of 3 criteria, c1, c2 and c3. My first thought was to
register a filter through a POST request.
Request :
POST /filters HTTP/1.1
c1=foo&c2=bar&c3=baz
Response :
HTTP/1.1 201 Created
Location: /filters/123456
But if I (or someone else) do it again, I'll end up with another
{filter-id}, e.g. 123457. Then we'll have two resources with the exact
same meaning and content.
If I want to use one URI for one criteria set, PUT seems more
appropriate. The thing is, the {filter-id} is assigned "randomly" by the
server. As a client, I can't guess nor force it. From there, I see the
following solutions, neither I find satisfying :
1/ I try to map artificially my URIs to the criteria set. I could use a
hash of the representation. e.g. :
Request :
PUT /filters/53c11504ba50e14b60407b13a72c0e53 HTTP/1.1
c1=foo&c2=bar&c3=baz
Response :
HTTP/1.1 200 OK
<form method="PUT"
action="/filters/53c11504ba50e14b60407b13a72c0e53">
<input name="c1" value="foo" />
<input name="c2" value="bar" />
<input name="c3" value="baz" />
<input type="submit" />
</form>
The problem is : it is artificial. If my criteria set changes, or if its
representation changes, I could end up with conflicts or doubles.
2/ I trick the client into believing it actually created something new,
whether or not it has. This means, even if /filters/123456 already
exists, my server will answer it to anyone POSTing the
"c1=foo&c2=bar&c3=baz" content.
The problem is : it's a fraud. Only the initial POST really "appends"
something.
3/ I answer the second-and-further POST requests with a "409 Conflict"
answer, and the correct location.
The problem is : it is not actually an error. The client doesn't care to
register something, it just has no way to know wheter or not the
resource he is looking for already exists.
4/ I do a preliminary check
Request :
GET /filters?c1=foo&c2=bar&c3=baz HTTP/1.1
Response :
HTTP/1.1 301 Moved Permanently
Location: /filters/123456
The problem is : If the resource doesn't exists at that time, I'll be
willing to create it. If I do, nothing ensures me that someone hasn't
created it inbetween. Then, if I want to put aside a location for my
resource, it implies my GET isn't safe anymore.
5/ The last option I see is that the URI should contains the whole
content of the resource, as it is its "natural" identifier. This is
especially difficult if I have a large set of criteria, and if their
values are complex. This is why we use ids in the first place.
Thanks in advance for your help
François