My thinking on this is to POST directly to the entry. Use forms-data or
JSON dictionaries where the absence of an element does not mean remove.
Bill
I thought this list was about JSON. ;)
> > I guess the more implicit message in this post is: UPDATE: there
isnt
> > an even remotely usable spec. With JSON at least its fairly easy to
> > just define new content fields and to null out dead ones. XML
doesnt
> > have a thing. Everything else is... well.. they are all their own
> > things, obviously theres no large scale way to address them.
>
> My thinking on this is to POST directly to the entry. Use forms-data
or
> JSON dictionaries where the absence of an element does not mean
remove.
With a hierarchical-URI approach, you simply define a new URI for the
piece(s) you wish to update. For example, if your "feed data" object at
/blog/1234 looks like:
{"title": "Gettysburg Address",
"author": "A. Lincoln",
"summary": "Four score..."}
...then you expose the URI's /blog/1234/title, /blog/1234/author, and
blog/1234/summary as "attribute resources" which receive single scalars
via PUT. You may also, of course, PUT the complete object to /blog/1234,
where the absence of an attribute means "supply a default" (not "skip
this field"; IMO this is the only sane interpretation of "transferring
resource representations"). In the rare case where the client wants to
send more than one attribute (but not the complete set) in a single
message, you define a subcollection with a name that expresses the
semantic intent, e.g. /blog/1234/metadata or /blog/1234/links. This is
the great thing about REST (and URI's in general): your hierarchy is
conceptual, and doesn't have to map 1:1 with the server implementation.
So group data in semantically-bounded bundles to your heart's content.
Robert Brewer
fuma...@aminus.org
Let's ignore PUT vs POST for the minute, I want to understand
the dynamics you are suggesting.
If a GET /foo returns:
{
"a": 5,
"b": "fred"
}
Then a POST /foo of:
{
"a": 6,
}
Means update "a" to 6, but leave "b" with it's current value?
How do you remove something, setting it to null?
For example a POST /foo of:
{
"a": null,
"b": "barney"
}
Does this mean remove "a" and update "b" to "barney"?
-joe
--
Joe Gregorio http://bitworking.org
Another idea (which we don't support)... should the POST in Joe's
example below to update /foo/a be the same as...
PUT /foo/a
6
(?)
And then for removing, maybe...
DELETE /foo/a
(?)
PUT /foo/a
6
Would be the same as a POST that updates the one attribute. For
updating more than one attribute at once, the json or forms-data via
POST to the object would be the more succinct protocol.
+1 (That's what I've been trying to explain...) Although I could read
"the same as" to mean "support both"; I don't think that's wise. "PUT
/foo/a 6" should be canonical.
> For updating more than one attribute at once, the json or forms-data
via
> POST to the object would be the more succinct protocol.
In my experience, this doesn't need to be generic; that is, there is
always a good, useful domain-specific semantic boundary to any attribute
subset. Give that subset a meaningful name (URI) and it becomes a new
resource in its own right, with its own smaller representation, and "PUT
/foo/vowels {'a': 6, 'e': 7, 'i': 9, 'o': 11, 'u': 17}" makes perfect
RESTful sense.
Robert Brewer
fuma...@aminus.org
I am not sure to understand the problem. Do you have any use case where you need to send updates to an URI without modifying the other attributes? It seems to me that most of the time, you GET a representation of the resource before PUTing a new representation to the server. What's the problem with sending the entire representation of the resource?
I'm not the one to say it's "RESTful" or not and I'm the first to say
it was done for a specific purpose in our case for a relatively
complicated data model for an app someone asked us to put together in
a couple of weeks.
On Mon, Aug 25, 2008 at 10:36 AM, Benoît Fleury <benoit...@gmail.com> wrote:
If this is a throwaway first pass I'd sidestep json altogether and use a
more traditional ws-*. Otherwise you risk getting bogged down writing custom
code and missing your delivery by hammering a spec into something it was
explicitly designed to not do. Just IMO but a hyper-aggressive delivery is
not the time to re-invent wheels.
--------------------------------------------------
From: "Patrick Logan" <patric...@gmail.com>
Sent: Monday, August 25, 2008 1:15 PM
To: <restfu...@googlegroups.com>
Subject: [restful-json] Re: POST: its not UPDATE
http://bitworking.org/news/296/How-To-Do-RESTful-Partial-Updates
--------------------------------------------------
From: "Dave Scheffer" <david.s...@gmail.com>
Sent: Monday, August 25, 2008 2:02 PM
To: <restfu...@googlegroups.com>
Subject: Re: [restful-json] Re: POST: its not UPDATE
This is not the forum for debating X vs. Y per se, but I will attest
that this approach *allowed* us to build the first pass very quickly.
I would continue along the likes of JSON-REST with the tools we've
been using for fast and reliable prototyping and evolution into
production.
Just saying, we're one data point.
For blog entries that is an inconvenience. For business transactions it is
potentially catastrophic.
I've come to agree that if highly granular attributes are the goal we should
treat them as their own resource.
Or chose another tool from the toolbox. WS-* fell over because it became
everyone's favorite hammer for things that look nothing like nails.
--------------------------------------------------
From: "rektide" <rek...@gmail.com>
Sent: Monday, August 25, 2008 2:59 PM
To: "restful-json" <restfu...@googlegroups.com>
Subject: [restful-json] Re: POST: its not UPDATE
>
and rektide replied:
> I'm not sure what you are trying to explain here and theres not enough
> context in your short example to make it clear.
>
> You appear to be disagreeing that there is a need for a PATCH/UPDATE?
Correct; there is no need for new verbs to break down a resource into
smaller resources if you can use new URI's to achieve the same effect.
> Or are you opposed to finer granularity object access? Or both?
In my head, an "object" is a list of attributes. I almost always need
support for changing those attributes without passing a representation
for the entire object. So I'm not opposed in principle. I am opposed to
inventing new verbs or sub-MIME-types to do that when giving each one
its own URI works quite well.
> Based off "domain specific semantic boundary," my first guess would be
> finer grain object access, but I dont see what your example has to do
> with that. Theres no subset context; vowels just kind of sits there
> by itself.
>
> Say I have a list of hotels in denver, with a subcategory of hotel
> type, with hotel info under that.
> /hotels/denver/mariott/1
>
> Would be the "new resource" in /hotels:
> {denver:{mariott: [{"Mariott Denver","16th street"}] }}
Probably not. Instead, 'denver' is just a subset of 'hotels' and
'denver/marriott' is a subset of that, so IMO a valid JSON
representation of the resource identified by the "/hotels" URI would be:
[{"name": "Marriott Denver",
"street1": "16th street",
"city": "denver",
"company": "marriott",
"href": "/hotels/1"},
{...},
{...}
]
A GET on "/hotels/1" would retrieve the first object in that list. In
other words, the "denver" and "marriott" path atoms are superfluous if
the desired object has a unique ID within the "hotels" collection. Not
only that, but they are not in a proper hierarchy; the company name is
not a subset of the city, so I would recommend rewriting
"/hotels/denver/marriott" to "/hotels?city=denver&company=marriott. As
collections get larger, their complete representations become cumbersome
to ship around, so top-level URI's like "/hotels" tend to grow lots of
search params in the querystring to return a subset of the objects in
the collection.
> You talk about giving smaller representations their own resource
> names, about how that is the ReSTful way to do it, and I agree
> wholeheartedly. I strongly believe there needs to be a _standard_
> way we should demarcate these smaller resources, especially in a JSON
> context where theres a semantic data/a clear hierarchy to recurse
> through. If you want smaller URL's, just use redirect aliases.
Yup. In the previous post, I was particularly pushing to expose
"attributes" (single scalar values) and "attributes subsets" as their
own resources. So I would expect "/hotels/1/company" to be represented
by the single JSON scalar value "marriott", and "/hotels/1/address" to
be represented by the JSON object {"street1": "16th street", "city":
"denver"}.
Robert Brewer
fuma...@aminus.org
Good question. It may simply be a difference in context. In my context I
work with primarily b2b and internal enterprise business problems. Context
and state are often implicit from a business workflow POV but from an
engineering perspective I have to focus more on scalability for highly
contested entities. Possibly you are in a more forgiving CRUD-like
environment.
This distribution list encompasses JSON in the REST model. Statelessness is
one of the four key principles to qualify it as a REST solution for an
engineering problem. I understand where you are going and why but diffgrams
are complex in all but the most trivial applications. It's not enough to
simply ID the entity I also have to qualify the change request so I can
sequentialize it. Even that is insufficient when I'm geo-scaled horizontally
across multiple farms.
That takes me from a simple "Update last name for this ID" to a more
laborious "Update last name for this ID Requested At Timestamp XYZ From
GeoLocation ABC Within Farm GHI By DataAdmin MNO WithPrivs QRS" - and all
the auditing, reconciliation and rollback overhead associated to it.
Sure you may not need all that but a spec has to take such things into
account. I'm just not sure why REST is the right application model anytime
I hear a form of diffgram.
It feels like the WS deathstar journey all over again. REST is about
subscribe/publish while minimizing cycles on client and server, JSON is
about minimizing bytes on the wire. There is a natural tension there that
diffgramming likely destabilizes. Warping REST away from its intent seems
silly when there are alternate more established models.
You're pushing towards a more tightly coupled application of REST JSON, one
that feels very RPC e.g. SET (id,attrA, attrB) VALUES (1234,'foo','bar').
That's what PATCH is for (and I feel PATCH is a hack). But then we get into
the headache of sequentializing highly contested resources.
How do I qualify contentious change requests? What if it is PayPal and not
Twitter?
*** Rather than taking the spec in a new direction I'd personally rather
just see the entity invalidated and replaced. I'll give up a few more bytes
on the wire if it means a simpler client and fewer cycles/state back in my
farm. Otherwise I'd go back to an SOA rather than ROA model.
SOA doesn't automatically mean fat XML/SOAPy payloads after all. And JSON
doesn't have to be RESTful.
Just IMHO, and I say that as someone who's done a fair number of loosely
coupled architectures using store and forwards like message queuing. Start
adding transactionality to the mix and it is Pandora's Box. Diffgraming
JSON over REST is an identical scenario. It is like someone using their
clutch to brake a car and therefore stressing a much more expensive
component so as to ease up on the cheaper one made for that purpose.
That gets us back to granularity. When some folks like me push back and
suggest if the attribute is really that significant make it a resource...
what we're also doing is opening the door on the thought experiment that
maybe the attribute is NOT significant and maybe REST is the wrong data
management model.
Sorry not trying to be argumentative. If someone needs to stovepipe their
app that's cool with me. As long as it stays in their sandbox. :)
Fair enough: /forums/{forum_id}/threads/{thread_id}/posts/{post_id}
The following would all return 2xx:
# Add a forum object to the forums collection
POST /forums
-> {"title": "Misc"}
<- 201 Created, Location: "/forums/1"
GET /forums
<- ["/forums/1"]
GET /forums/1
<- {"title": "Misc", "threads": []}
# Add a thread object to the forums/1/threads collection
POST /forums/1/threads
-> {"title": "OSX sockets"}
<- 201 Created, Location: "/forums/1/threads/1"
GET /forums/1/threads
<- ["/forums/1/threads/1"]
GET /forums/1/threads/1
<- {"title": "OSX sockets", "posts": []}
# Add a post object to the forums/1/threads/1/posts collection
POST /forums/1/threads/1/posts
-> {"title": "A silly post",
"content": "ROFLMAO"}
<- 201 Created, Location: "/forums/1/threads/1/posts/1"
GET /forums/1/threads/1/posts
<- ["/forums/1/threads/1/posts/1"]
GET /forums/1/threads/1/posts/1
<- {"title": "A silly post",
"content": "ROFLMAO"}
PUT /forums/1/threads/1/posts/1/content -> "LOL"
GET /forums/1/threads/1/posts/1/content <- "LOL"
# Add a couple more posts to the same thread
POST /forums/1/threads/1/posts
-> {"title": "Nothing special", "content": "No, really."}
<- 201 Created, Location: "/forums/1/threads/1/posts/2"
PUT /forums/1/threads/1/posts/3
-> {"title": "AHA", "content": "aha"}
<- 200 OK
GET /forums/threads/posts/content <- ["LOL", "No, really.", "aha"]
> Your recommended access rewriting (using GET query strings) conflicts
> with the POST syntax you used for updates below.
Err, I didn't mention POST at all, but I think you're noting that nobody
would ever expect to POST anything to
"/hotels?city=denver&company=marriott", which is absolutely correct.
Reads often have wildly different call requirements (and therefore
URI's) than writes in this way. You could use that same URI with PUT for
batch replace, but it's rare. DELETE is more common in my experience
(for batch deletes).
Robert Brewer
fuma...@aminus.org
> Err, I didn't mention POST at all, but I think you're noting that nobody
> would ever expect to POST anything to
> "/hotels?city=denver&company=marriott", which is absolutely correct.
Au contraire. I would expect any object POSTed through that
restricted collection to have its city and company fields set
accordingly, if not present in the POSTed object. This is the REST
equivalent of updating through a view in relational databases, and a
very nice feature indeed.
--
GMail doesn't have rotating .sigs, but you can see mine at
http://www.ccil.org/~cowan/signatures
Could you please quote more? I read this in email, not
groups.google.com, so it's difficult to know to whom you are replying.
Robert Brewer
fuma...@aminus.org
That would return the JSON object
{"title": "OSX sockets",
"posts": [
"/forums/1/threads/1/posts/1",
"/forums/1/threads/1/posts/2",
"/forums/1/threads/1/posts/3",
]}
> GET /forums/1/threads/1/title
That would return "OSX sockets" as a single scalar.
Robert Brewer
fuma...@aminus.org
--------------------------------------------------
From: "rektide" <rek...@gmail.com>
Sent: Monday, August 25, 2008 6:13 PM
To: "restful-json" <restfu...@googlegroups.com>
Subject: [restful-json] Re: POST: its not UPDATE
>
> With a hierarchical-URI approach, you simply define a new URI for the
> piece(s) you wish to update. For example, if your "feed data" object at
> /blog/1234 looks like:
>
> {"title": "Gettysburg Address",
> "author": "A. Lincoln",
> "summary": "Four score..."}
>
> ...then you expose the URI's /blog/1234/title, /blog/1234/author, and
> blog/1234/summary as "attribute resources" which receive single scalars
> via PUT. You may also, of course, PUT the complete object to /blog/1234,
> where the absence of an attribute means "supply a default" (not "skip
> this field"; IMO this is the only sane interpretation of "transferring
> resource representations").
I've seen variations of this approach proposed over the last year or so.
IMO it's not going to work. If you want data elements to have URIs, use
RDF. RDF will even deal with cases where the relationship is not a
membership or an aggregate.
> In the rare case where the client wants to
> send more than one attribute (but not the complete set) in a single
> message, you define a subcollection with a name that expresses the
> semantic intent, e.g. /blog/1234/metadata or /blog/1234/links.
It's not a rare case. Hence we have forms-data. And this is the problem.
The industry won't adopt something like RDF, and AtomPub doesn't cater
for partial updates. I know what this list is called, but imo a
technology that enables partial updates while remaining palatable to
programmers is *the* problem this group needs to solve.
Bill
Bill
> How do you remove something, setting it to null?
> For example a POST /foo of:
>
> {
> "a": null,
> "b": "barney"
> }
>
> Does this mean remove "a" and update "b" to "barney"?
{
"a": "$null",
"b": "barney"
}
you need reserved syntax for it, yes. Removing data should be explicit
not implicit.
Bill
So you're saying I should use RDF but I can't use RDF. :) Perhaps you
could outline an alternative.
Robert Brewer
fuma...@aminus.org
> So you're saying I should use RDF but I can't use RDF. :)
Wrong conclusion. I'm saying that if you want to give XML elements or
JSON keys URLs, then look at how RDF manages graphs using URIs, lest you
waste your time.
> Perhaps you could outline an alternative.
Allow partial updates of data using JSON dictionaries.
Bill
OK, agreed on the basic principle that removing data should be explicit and
not implicit.
-joe
--
Joe Gregorio http://bitworking.org