Allowing methods to be called by the RESTful API

255 views
Skip to first unread message

Andrew O'Neil

unread,
Mar 16, 2009, 6:58:38 PM3/16/09
to silverst...@groups.google.com
I've just committed a changeset to enable access to call methods via the RESTful api. You can see this changeset here:

http://open.silverstripe.org/changeset/73149

A method can be called using the following HTTP request:

POST /api/v1/(ClassName)/(ID)/(MethodName) - executes a method on the given object (e.g, publish)

To allow a method to be called on a DataObject, you must add it to a static $allowed_actions array.

What's not obvious is what should be returned by the request. At the moment it returns a HTTP 204 No Content response. We probably want to include the response of the method call - what are your thoughts on this?

Sigurd Magnusson

unread,
Mar 16, 2009, 7:26:58 PM3/16/09
to silverst...@googlegroups.com
Andrew, regarding the restful API how does someone learn of the possible classes and IDs?

It seems like you would want a list classes and list IDs (GET) api, although, if thought needs to go into how to inform an API user that there is, say, 1 million page IDs without a CPU or network hit.

Sig

Hamish Campbell

unread,
Mar 21, 2009, 12:44:35 AM3/21/09
to SilverStripe Development
Sig: seems a bit OTT. Usually if you are using a REST API you already
know the classes. What would the benefit be? APIs are usually a bit
useless without documentation anyway. W/regards getting the total
number, the DataObjectSet tag returns the total in the set as an
attribute, even if the returned result is smaller. Maybe that could be
extended, but again, it's slightly unusual behaviour from a REST point
of view. Perhaps there could be a default method like /info that
returns a standard response, eg with total number, most recently
modified, etc.

Andrew: Can I suggest that if a method is successful return 200 with
some appropriate XML. Eg:

<MethodResponse>
<Code>200</Code>
<Result>True</Result>
<Message>This was returned from the method call</Message>
</MethodResponse>

And if it fails, a 400 with the same format:

<MethodResponse>
<Code>400</Code>
<Result>False</Result>
<Message>This was returned from the method call, or maybe some other
error message from the API</Message>
</MethodResponse>

Mark Rickerby

unread,
Mar 21, 2009, 1:47:40 AM3/21/09
to silverst...@googlegroups.com
If you are going to use <MethodResponse> <Message> type elements in
the response, it's not RESTful.

Please don't call it a REST API, when it is clearly an RPC API. It's confusing.

Ingo Schommer

unread,
Mar 21, 2009, 5:28:59 AM3/21/09
to SilverStripe Development
Agree with Mark in principle, thats exactly why you get into problems
on what to display in the result, because its not clear what resource
or set of resources you're modifying
in the method call. In SilverStripe, a method could return a pseudo-
relation (DataObject or ComponentSet), in which case I think its fine
to route through these calls to RESTfulServer. We have to solve the
problem of defining those dynamic getters as "pseudo-$has_many" etc.
on a higher level anyway, ideally RESTfulServer wouldn't even know the
difference.

But apart from the hardliner approach on being RESTful, we have to
deal with a CMS datamodel thats not build around having everything
packaged in resources, e.g. a "publication". So do we disallow even
the most basic things like "publish" until we adapt a more restful
datamodel (which will most likely not happen anytime soon), or are we
pragmatic about it?
Or to turn it around: How do we avoid encouraging bad REST practices
while still having a workable intermediate implementation for the CMS
datamodel?

See http://en.wikipedia.org/wiki/Representational_State_Transfer#REST_versus_RPC
for an overview on REST vs. RPC

BTW, a quite similiar discussion over at Zend ;)
http://framework.zend.com/issues/browse/ZF-5027

Ingo Schommer

unread,
Mar 21, 2009, 5:49:48 AM3/21/09
to SilverStripe Development
And some more related stuff on larger efforts to standardize on a
pseudo-restful
document publication protocol based around atom: CMSIS. Seems to be a
fail,
but highlights roughly the same problems we're currently having in
describing a "publication" in RPC-style.
Roy Fielding has some interesting thoughts around it:
http://roy.gbiv.com/untangled/2008/no-rest-in-cmis

In the end, a RESTful publication would be a PUT to the /live/Page/
<ID> resource,
which happens to be a copy of the GET /stage/Page/<ID> resource,
right?
We currently don't support this style of doing things though, as
there's more hooks
into the publish() method, e.g. writing to the versions table, or
calling onBeforePublish()
on decorators.

On Mar 21, 10:28 am, Ingo Schommer <ingo.schom...@gmail.com> wrote:
> Agree with Mark in principle, thats exactly why you get into problems
> on what to display in the result, because its not clear what resource
> or set of resources you're modifying
> in the method call. In SilverStripe, a method could return a pseudo-
> relation (DataObject or ComponentSet), in which case I think its fine
> to route through these calls to RESTfulServer. We have to solve the
> problem of defining those dynamic getters as "pseudo-$has_many" etc.
> on a higher level anyway, ideally RESTfulServer wouldn't even know the
> difference.
>
> But apart from the hardliner approach on being RESTful, we have to
> deal with a CMS datamodel thats not build around having everything
> packaged in resources, e.g. a "publication". So do we disallow even
> the most basic things like "publish" until we adapt a more restful
> datamodel (which will most likely not happen anytime soon), or are we
> pragmatic about it?
> Or to turn it around: How do we avoid encouraging bad REST practices
> while still having a workable intermediate implementation for the CMS
> datamodel?
>
> Seehttp://en.wikipedia.org/wiki/Representational_State_Transfer#REST_ver...
> for an overview on REST vs. RPC
>
> BTW, a quite similiar discussion over at Zend ;)http://framework.zend.com/issues/browse/ZF-5027

Mark Rickerby

unread,
Mar 21, 2009, 5:56:42 AM3/21/09
to silverst...@googlegroups.com
If you're going to go in this direction, I think you should just have
a REST API and an RPC API - there are different use cases for each.

You would want to focus on noun based resource access for loosely
coupled systems (ie: one server supplying many clients).

The RPC stuff calling actions, running methods etc, seems to me to be
more tightly coupled (ie: the methods are accessible directly through
the URL, that's about as tightly coupled as it can get) better
optimized for 1-1 web services or private/secure network
communication.

Ideally, SilverStripe would provide the basic tools and patterns that
enable apps to use either paradigm, I don't think it's necessary to
take a hardline RESTful stance - however, I do think it's good you
recognize that REST is a good approach to use building websites in
general, and encourage this with the basic data access patterns.

However, RPC might be an easier and more pragmatic approach for
certain use cases. The main issue I would say is the tight coupling,
but again, this might be ok for many scenarios, where a pure model
would be more opaque and subtle. The RPC way just allows you to tweak
parameters and slam the API directly with very little abstract
thinking required.

Mark Rickerby

unread,
Mar 21, 2009, 5:59:46 AM3/21/09
to silverst...@googlegroups.com
Hi Ingo,

Or to rephrase my last email: once the cat is out of the bag (ie: a
data object RPC API is in the wild), people will start using it, and
it's going to be much harder down the line to rearchitect it in a
RESTful style, if we ever figure out how to resolve the problems with
natural "verb actions" you mention, like publish, etc.

Mark Rickerby

unread,
Mar 21, 2009, 8:34:01 PM3/21/09
to silverst...@googlegroups.com

Hamish Campbell

unread,
Mar 22, 2009, 4:00:47 PM3/22/09
to SilverStripe Development
Ah, yes. Not a REST expert, obviously, but how do you know if your
method call has succeeded or failed? Or would you need to GET a
response to see if the method had the desired effect?

I'm using the current RestfulService in a live project at the moment -
just a one-way push from a customers database to their website - but
it is incredible useful already. Looking forward to seeing future
developments.

On Mar 22, 1:34 pm, Mark Rickerby <core...@gmail.com> wrote:
> http://www.tbray.org/ongoing/When/200x/2009/03/20/Rest-Casuistry

Mark Rickerby

unread,
Mar 22, 2009, 9:21:12 PM3/22/09
to silverst...@googlegroups.com
Good question Hamish...

In general, you should be using the HTTP status codes to signal the
result of an operation.

Creating a new object should return a 201 if the request succeeded.
Ideally, the response body would provide a URI that you could then GET
to see the new object. Opinions may vary, but I don't see anything
wrong with also returning the main fields of the object data in this
response too.

Use status code 202 to indicate that a request has been accepted by
the server, but may not necessarily be able to return a result
immediately. For example, if you triggered a method to clear a cache
or do a whole bunch of ongoing changes out of band that take too long
to just tell you the response immediately. 202 means your client now
knows that the operation is "in progress", again, it needs to check
back with a GET on whatever status URI to confirm the changed state.
This is the achilles heel of HTTP - there's no way for the server to
"ping" back to the client to tell you that it has finished what you
asked it to do.

(do a google search for "http comet" to find one particular workaround
for this).

The tricky thing is in the details, ie: *what does it mean for an
operation to succeed or fail*... This is internal to your app, it
can't be answered at the protocol level.

RPC architectures have a similar mismatch to the ORM impedance
mismatch between objects and databases. That is, with setter methods
that update an object state, local method calls often return void, and
throw an exception if the method fails.

But across a network with messages, you can't really fire a request
into the void, and not get any answer.

But getting back to the question of what it means to succeed or fail,
this could be different, depending on what the method call is, and all
the side effects that it has on the system. Maybe it is just updating
the state of a single object, but maybe it's actually touching a whole
bunch of other things. There isn't a general case, you have to look at
the specific method and what it is doing.

Hope that makes some kind of sense.

Sam Minnee

unread,
Mar 22, 2009, 9:59:45 PM3/22/09
to SilverStripe Development
It seems to be that it's impossible to support publication in a
'purely RESTful' API. We seem to have a choice between two
alternatives:
#1 Don't include publication in the purely RESTful API.
#2 Include publication in some non-standard way, while still keeping
the regular read/write operations of the RESTful API "pure".

#2 seems like a far better option for people, #1 seems like a tragedy
of idealism over pragmatism. This is what Andrew's suggestion appears
to support.

I suppose that there is option #3: a more esoteric way of looking at
it, where you consider that every page has a collection of "live
changes", and that you push an extra publication onto this chain to
create a publication. In the examples below, I have used the "POST"
verb to mean "append an item to a collection", that is, "post" as in
"comment".

Pushing an empty publication could publish immediatey:

POST SiteTree/3/LiveChanges.xml
<Publication />

Pushing a publication with a date could execute an embargo operation:

POST SiteTree/3/LiveChanges.xml
<Publication When="2009-10-10" />


In addition to posting a <Publication>, one could post a <Removal> in
order to unpublish a apge.

POST SiteTree/3/LiveChanges.xml
<Removal />

A removal with a date becomes an expiry

POST SiteTree/3/LiveChanges.xml
<Removal When="2009-10-10" />

Finally, getting the LiveChanges.xml will give you a list of all of
the historic changes

GET SiteTree/3/LiveChanges.xml
<Changes>
<Publication When="2009-01-01" />
<Publication When="2009-01-02" />
<Publication When="2009-01-03" />
<Removal When="2009-01-04" />
</Changes>

But all this turning of verbs into nouns reminds me a little too much
of Java at its worst, and I question what we actually gain from option
#3, beyond some vague notion of "purity".

Mark Rickerby

unread,
Mar 22, 2009, 10:40:53 PM3/22/09
to silverst...@googlegroups.com
In essence, SiteTree is a noun, there's nothing artificial about that.
I would suggest posting to a generic resource that represents the
entire SiteTree to publish and unpublish...

POST /SiteTree/Live
<Page PageID="3" />

To unpublish:

POST /SiteTree/Draft
<Page ID="3" />

GET /SiteTree/Live

<SiteTree PublicationStatus="Live">
<Page ID="3" When="2009-01-01" />
<Page ID="4" When="2009-01-02" />
</SiteTree>

GET /SiteTree/Live/Changes

<Changes PublicationStatus="Live">
<Publication ID="3" When="2009-01-01" />
<Removal ID="3" When="2009-01-04" />
</Changes>

Or for an individual page:

GET /SiteTree/3/Changes

<Changes ID="3" PublicationStatus="Draft">
<Publication When="2009-01-01" />

Sam Minnee

unread,
Mar 22, 2009, 11:03:12 PM3/22/09
to SilverStripe Development
The general idea in your proposal seems to be that you POST (or should
we PUT?) to /SiteTree/Live some XML representation of "these pages
from the draft site"

I like that idea, however I find the specific XML you've used a little
arbitrary. It would probably be better to recycle the existing XML
as much as possible, with a few provisos:

* Use a key, Stage="Stage", to represent "this page from the stage
site". Version="36" could also be used to get a specific version #.
* Allow very sparse objects; assume that other values will come from
the existing content inside the database.

That would lead us to the following request for publishing pages #3,
#5, and #6:

PUT /SiteTree/Live
<DataObjectSet>
<SiteTree ID="3" Stage="Stage" />
<SiteTree ID="5" Stage="Stage" />
<SiteTree ID="6" Stage="Stage" />
</DataObjectSet>

One potential issue with this is that we're kind of lying about what
this method actually does. For instance, one would hope that this
request is going to call the onAfterPublish method of each page.

Still, you're unlikely to be able to hook this into some 3rd party
tool without specifically designing it for SilverStripe, which kind of
defeats the purpose a little.

Mark Rickerby

unread,
Mar 22, 2009, 11:40:36 PM3/22/09
to silverst...@googlegroups.com
Hi Sam,

> Still, you're unlikely to be able to hook this into some 3rd party
> tool without specifically designing it for SilverStripe, which kind of
> defeats the purpose a little.

If that is the intent, then it's best to go with the Atom Publishing
Protocol, since that's the only standard of this type that seems to
have widely available tools... the alternatives are even more esoteric
than the ideas being discussed in this thread.

http://www.ibm.com/developerworks/library/x-atompp1/

eg: it's natively supported by browsers like Flock, and I think
Windows Live Writer does it as well.

Sigurd Magnusson

unread,
Mar 22, 2009, 11:44:29 PM3/22/09
to silverst...@googlegroups.com
On 21/03/2009, at 5:44 PM, Hamish Campbell wrote:

> Sig: seems a bit OTT. Usually if you are using a REST API you already
> know the classes. What would the benefit be? APIs are usually a bit
> useless without documentation anyway

This would help towards self-documentation, provides help in debugging
situations, leads to discovery of new ways to use the data, especially
if the API is public.

Sigurd

Sam Minnee

unread,
Mar 22, 2009, 11:45:12 PM3/22/09
to SilverStripe Development
> If that is the intent, then it's best to go with the Atom Publishing
> Protocol, since that's the only standard of this type that seems to
> have widely available tools... the alternatives are even more esoteric
> than the ideas being discussed in this thread.
>
> http://www.ibm.com/developerworks/library/x-atompp1/

That's not a bad idea... Does the AtomPP support draft/published
content? What about hierarchical collections?

Mark Rickerby

unread,
Mar 22, 2009, 11:52:48 PM3/22/09
to silverst...@googlegroups.com
LOL, the hierarchical collections was my next point... this is
unsupported AFAIK, but I believe people have developed extensions to
the format for this:

http://www.oracle.com/technology/tech/feeds/spec/draft-divilly-atompub-hierarchy-00.html

I would assume that draft/published would just be treated as separate
collections.

This http://code.google.com/apis/gdata/ is also an extension of the
APP I believe.

Sam Minnee

unread,
Mar 23, 2009, 12:00:29 AM3/23/09
to SilverStripe Development
On Mar 23, 4:52 pm, Mark Rickerby <core...@gmail.com> wrote:
> I would assume that draft/published would just be treated as separate
> collections.

Which brings us full-circle. This is a bone-headed way of treating
publication. How would the client expose a 'publish' button if draft
& published were assumed to be separate collections? Are you expected
to copy your draft page from one blog and paste it into the published
one?

Any API needs to have publication as a first-class action, or at least
some globally understood convention that communicates that.

I'm thinking maybe it's time to admit that pure REST fails, that not
everything can be represented as behaviour-less data, and go with
Andrew's original solution.

Mark Rickerby

unread,
Mar 23, 2009, 12:05:13 AM3/23/09
to silverst...@googlegroups.com
Sorry, I was wrong on that:

http://atomenabled.org/developers/protocol/#appDraft

Turns out this is represented by a draft element on the entry itself.
The draft/live collections would just be a 'nice to have' feature, if
you wanted to expose the queries/lists to clients.

Reply all
Reply to author
Forward
0 new messages