On 2010-12-02 17.07, Jerome Louvel wrote:
> Hi Rickard,
>
> This was indeed a bug. It is now fixed in SVN 2.0 branch and trunk.
> Thanks for the report.
Goodie, thanks!
> Regarding the TunnelService, it can automatically replace the
> "Accept" header sent by broken clients with a more sensible one,
> based in the "UserAgent" header value. See the TunnelService Javadocs
> for more details.
Ok, then I understand, thanks.
> Could you elaborate a bit more on what is lacking in ServerResource
> from your point of view? We have plans to support HATEOS more
> automatically in 2.2 so this might help. Any pointer to your
> suggested approach (UseCases?).
Basically it comes down to very fundamental issues with what people tend
expose as their REST API (domain model=VERY BAD IDEA!), and the problems
that come from that, and what to do instead. And once you start
realizing that HATEOAS is superimportant, and only really works if you
expose usecases instead, and that it should be as native as possible in
code, that also changes things.
What we have done is to do sort of a custom router mechanism. Let's say
we have the following URL:
/account/changepassword
In my current code this is handled by first having a root resource that
represents "/" with:
public class RootResource
extends CommandQueryResource
{
@SubResource
public void account()
{
subResource( AccountResource.class );
}
... more subresources ...
}
So a Restlet will instantiate RootResource and call handle() on it,
which through reflect will find "account" and call that, which
instantiates AccountResource, which looks like this:
public class AccountResource
extends CommandQueryResource
{
public AccountResource( )
{
super( AccountContext.class );
}
... more subresources to /account/ ...
}
/account/ represents a usecase, and now "changepassword" is one of the
interactions in this usecase. The AccountContext finally contains the
actual code for the usecase (the *Resource is on the REST level, not the
usecase level):
public class AccountContext
{
@Structure
Module module;
public void changepassword( ChangePasswordCommand newPassword )
throws WrongPasswordException
{
UserAuthentication user = RoleMap.role( UserAuthentication.class );
user.changePassword( newPassword.oldPassword().get(), newPassword
.newPassword().get() );
}
}
Where ChangePasswordCommand is a value object that will be
created+filled in with form data (either POST'ed or from request query
parameters). If I do GET on /account/changepassword I get a form with
the required fields (oldPassword, newPassword).
And that's all. Now I don't have to bother with much RESTy stuff in my
usecase code, and yet I get my REST API constructed more or less
automatically.
Additionally, a core requirement in REST is that the client has to find
it's way to this URL by following links. Given the above code my
framework can automatically generate HTML/JSON with links for every path
that ends with "/". So from "/" you would get HTML that includes:
Account
and from "/account/" you would get HTML that includes:
changepassword
With this the client can find the URL without having to know the URL
structure, only rel attributes, which is how it should be. This allow me
to restructure the API without having to change clients interpretation
of paths and whatnot, which is a key point of HATEOAS.
So this is how we write our application code, with Restlet handling all
the RESTy details of parsing requests and generating responses. Which is
perfect!
With this as background, ServerResource is (IMO) simply too low-level,
and exposes things that for most application API's should not be
bothered with. I still use it for other parts of my API, but not the
core application-level part.
For more details see:
http://www.jroller.com/rickard/entry/rest_api_for_infrastructure_domain
http://www.jroller.com/rickard/entry/rest_and_the_missing_link
/Rickard
--
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=2687242