On Sat, Mar 6, 2010 at 2:33 PM, J. Shirley <jshir...@gmail.com> wrote:
This is where I actually really love ::REST. If you make a JSON call, > then whatever is in the stash key that ::REST is returned to the > client (via ->status_ok) -- but if you combine this with chained and > setup other data in the stash, the TT view can access all of that. > That sounds good. So, what does that exactly look like? Can you help with a few examples? These are the goals: - Don't repeat action code. i.e. don't have separate actions for REST and web requests for the same resource. - Same URLs for the same data (e.g. /blog/recent_posts) for both the REST actions and when rendering with TT. - Separate out View from Controller. i.e. don't have actions build data structures that won't be used in the response. - Web browser requests differ from REST requests, so may need to map the request into a common format before running the action. - Clean and tidy controllers ;) Here's two very simple actions. The first is in a REST app that needs to add a TT view. The second is an existing web (browser) action that need to extend to accept JSON input and generate JSON output (i.e. convert to using ::REST). First, here's a pseudo REST action for recent blog posts. How would you modify or extend so it works with a TT view? Assume that the TT view needs more data from the posts (i.e. include \...@posts in the stash), and always returns a 200 since the page will always have content. Also, the TT view has no need for the "entity" structure created in this existing action: sub recent_posts : Local ActionClass( 'REST' ) {} sub recent_posts_GET { my ( $self, $c ) = @_; my @posts = map { { id => $_->id, body => $_->description, created => $_->created_time->iso8601_with_zone, } } $c->model( 'Blog::Post' )->recent; return @posts ? $self->status_ok( $c, entity => { posts => \...@posts } ) : $self->status_no_content( $c ) unless @posts; } Or going the other direction, here's an existing action used by web browsers to view a blog, create a blog, or update an existing one. A web form POSTs to /blog to create and POSTs to /blog/1234 to update. The request parameters are validated and used to update/create the blog. The action builds a "$form" object that used for this purpose and is used by TT to render the form. Note that a GET to this action works, too. It simply returns a form object in the stash which TT can use to display the post (or can get at the blog object directly with $form->object). But, would be better to chain from an action with Args(1). sub blog : Path { my ( $self, $c, $id ) = @_; # is user authorized for this method? return $c->res->status( 403 ) unless $self->authorize( $c ): # build form object and place in stash; my $form = Form::Blog->new( $id, # will be undefined for CREATE $c->req->parameters, ); $c->stash->{form} = $form; # GET requires an id to a valid blog return $c->status( 404 ) if $c->req->method eq 'GET' && !$form->object; # was form POSTed? return unless $c->req->method eq 'POST'; # redisplay page if does not validate return unless $form->validate; # write data to model $form->update_or_create; # Redirect to show new post: my $uri = $c->uri_for( 'blog', $form->object->id ); return $c->res->redirect( $uri ); } Now, for REST it's a POST to /blog to create and NO arguments are allowed, and for update it's a PUT to /blog/$id and $id is REQUIRED. The request parameters are in a "blog_data" key in the JSON request. For create return 201 with a Location: header. On update probably return a 204. If the request parameters do not validate (as tested by the form) then need to serialize the form's errors in a JSON response. Much of the existing blog action can be used for both the POST and PUT (and probably GET), but need to map the "blog_data" into parameters used by the form object. That seems a bit more straight forward -- just add blog_<$METHOD> methods that (re-set) the status code (and for GET) builds the "rest" stash. But, still need a way to extract out the "blog_data" from $c->req->data and use it as the request parameters. > Just curious, but if you apply the REST action class (and define > _GET/_POST methods) and keep stash population to your actions, what is > failing there? I've done similar things to what you are discussing > (and will again in about a week or two from now) and hadn't run into > anything severe. > The main issue is just finding a clean way to handle both web browser and REST request and responses. But specifically, two issues: First, input munging is often required. I want to share action code for processing input data but the web request comes in $c->req->parameters, but with ::REST the request data comes in $c->req->data and may be formatted slightly differently or with different parameter names. Second, the ::REST approach has the controller actions building the "entity" structure which feels more like a view operation. Minor point but turning, say, a DateTime object into a human-readable format (or ISO8601 for JSON response) both seem like views. -- Bill Moseley mose...@hank.org
_______________________________________________ List: Catalyst@lists.scsys.co.uk Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst Searchable archive: http://www.mail-archive.com/catalyst@lists.scsys.co.uk/ Dev site: http://dev.catalyst.perl.org/