[Catalyst] Best practices: XML output from static XML

Bill Moseley moseley at hank.org
Sat Mar 6 22:20:52 GMT 2010


On Sat, Mar 6, 2010 at 12:59 PM, J. Shirley <jshirley at gmail.com> wrote:

>
> I don't think the path taken with Catalyst::Action::REST is the best,
> but it does work very very well in my opinion and I certainly can't
> think of anything better.  Being able to send to a serialization
> method based on the content-type solves a lot of these issues.  You
> could just setup a content type for your feed in configuration and
> write a custom serialize class and get exactly what you are asking
> for.
>

I agree, ::REST works well for what it does, but doesn't provide a framework
for a View that is action-specific.  ::REST assumes that the actions set an
entity in the "rest" stash that can be serialized.  But, if the same action
is used for a TT View then probably want to just pass model objects in the
stash instead of creating a "rest" structure in the stash.

One could argue that actions should always result in the same data in the
stash regardless of the View (the Controller has no idea about the final
View).  The reality is that a request to /blob/recent_posts would return
different data in a JSON response than would be use to render a web page
with a TT view.



The "problem" I'm up against is we have an existing application written for
the web -- so actions expect GET and POST requests and place objects in the
stash.  Then TT uses the objects in the stash to render the markup.

Now we need to expose these same methods (which means same URLs really) for
two similar purposes.  New development for the web app is all client side.
 Fat AJAX that talks to the application via JSON serialized (mostly)
requests and responses.  Plus, we need to expose a REST API for third party
customers.  So, really it's just an API for both.

::REST will work fine for new actions, but there's a lot of existing actions
that need to work both for TT rendered pages and for JSON responses.

I think the action's job should be to take a request, validate,
authenticate, authorize, etc, then either generate error or place model
objects in the stash.  Then pass off to the view to render/serialize.

The problem is that a request coming from a browser may be slightly
different than from an AJAX or API request.  (Request might come in a as a
POST on the web and a PUT via the API and parameters might be slightly
different.)  So, either need to dispatch to different actions for same
request URL or have some kind of filtering code that runs before the action
to "normalize" the request for the action depending on where it's coming
from.

Obviously, it makes sense to share the actions where possible.

Likewise, the TT View passes control to an action-specific template to
render the markup for the request, but the same action might need to return
JSON  so in that case would need to also have action-specific code to build
the json stash from the objects the action fetched.

I don't want to evangelize ::REST too much, so to address your

suggestions more directly I'd have to say that relying on $self->can

 seems a bit too limited for my tastes.


I don't like it either.  Still, to me it seems the act of taking the model
objects loaded by the controller and building the "json" stash is a View
action -- not something that should happen in the controller action.


 I'd lean on configuration more so than $self->can.  Then a call to
> $self->get_serializer_for('JSON') that returns some serialization
> object (or whatever handler you have) is simple, and coupling it with
> Moose would work very well.  Then you can work out adding new
> serialization calls just in config.
>

So are you suggesting that $self->get_serializer_for('JSON') would return
code that would be action-specific?  That is for a request to
/blog/recent_posts would return code that would know what to put in the
"json" stash for that specific request?

I like the idea of leaning on the configuration implementation -- just not
sure what that looks like. ;)




> However, I'm having a hard time thinking about any valid use cases for
> this, especially since ::REST does things fairly well (especially for
> how old the code is) so I'd automatically use that for all the cases I
> can think of. Anything else that doesn't fit, I'd just defer to having
> separate views (and possibly a different RenderView+end action as
> appropriate).
>

If I was starting fresh I'd be tempted to write all the controller actions
with ::REST.  I'd like it if the various _<METHOD> actions were real actions
for dispatching (they aren't right?).  Real actions would mean could test
things like Args:

     sub blog_GET : Local Args(1) {  # GET requires an argument
     sub blog_POST: Local Args(0)  { # POST creates and must not have an
argument
     sub blog_PUT : Local Args(1) {  # PUT requires and argument
     sub blog_DELETE : Local Args(1) {

Then layer the web app on top -- especially if it's all client side.  But,
again I still think the action's job would be to just place model objects in
the stash -- not build a "json" structure as that is only needed when the
response is actually json.



-- =

Bill Moseley
moseley at hank.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20100306/be963=
2f5/attachment.htm


More information about the Catalyst mailing list