Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use Catalyst and uri chaining with a REST interface?

Tags:

perl

catalyst

I'm expecting to receive uri's like

/user/*/account/*

I've got a user function defined as

sub user  :Path('/user') :PathPart('') :ActionClass('REST' ) {}

then

sub user_GET :PathPart('user') Chained('/') CaptureArgs(1) {
    #do stuff

}

For accounts I'm defining them similarly.

 sub account :Path('/account') :PathPart('') :ActionClass('REST') {}

 sub account_GET :PathPart('account') Chained('user_GET') Args(1) {
     #do stuff
 }

So, the problem is when I set Chained in account_GET to 'user_GET' the server debug show that the path is set:

[debug] Loaded Chained actions:
.-----------------------------+--------------------------------------.
| Path Spec                   | Private                              |
+-----------------------------+--------------------------------------+
| /user/*/account/*           | /mcp/user_GET (1)                    |
|                             | => /mcp/account_GET                  |
'-----------------------------+--------------------------------------'

When I set Chained in account_GET to 'user' the server debug shows:

[debug] Unattached Chained actions:

[debug] Unattached Chained actions:

.-------------------------------------+--------------------------------------.
| Private                             | Missing parent                       |
+-------------------------------------+--------------------------------------+
| /mcp/account_GET                    | /mcp/user                            |
'-------------------------------------+--------------------------------------'

The problem is that clearly that latter isn't being set up and the former is returning that it wasn't found.

So the problem is if I'm calling /user/12345/account/23456 how do I get that path set correctly when what appears to be the obvious path, Chained('user'), isn't being set and the less obvious path, Chained('user_GET'), simply isn't working?

like image 802
jmcneirney Avatar asked Dec 13 '11 17:12

jmcneirney


People also ask

How should REST API designers design URIs for clients?

Clients must follow the linking paradigm of the Web and treat URIs as opaque identifiers. REST API designers should create URIs that convey a REST API’s resource model to its potential client developers. In this post, I will try to introduce a set of design rules for REST API URIs.

What happens if two URIs are different in REST API?

Two different URIs map to two different resources. If the URIs differ, then so do the resources, and vice versa. Therefore, a REST API must generate and communicate clean URIs and should be intolerant of any client’s attempts to identify a resource imprecisely.

How should REST API clients decide what content format to use?

Instead, they should rely on the media type, as communicated through the Content-Type header, to determine how to process the body’s content. File extensions should not be used to indicate format preference. REST API clients should be encouraged to utilize HTTP’s provided format selection mechanism, the Accept request header.

What is a URI URL?

URI: The Uniform Resource Identifier Explained. The concept of URLs is familiar to most people. A URL is a web address that is used to direct users to websites on the internet. But what is a URI? The concept of URIs was conceived of by the forefather of the World Wide Web, Tim Berners-Lee.


1 Answers

Personally, I'd go for something like the following in the user controller:

package MyApp::Controller::User;
...
# root of the chain
sub object: Chained PathPart('user') CaptureArgs(1) { ... }

The object action above would load the user object into the stash. Now I'd have the user controller chained off the above like the following:

package MyApp::Controller::User::Account;
...
# chains to the action loading the user object and dispatches RESTy
sub account: Chained('/user/object') ActionClass('REST') Args(1) { ... }

# handle individual request methods
sub account_GET { ... }
sub account_POST { ... }

Here the account action provides common data for the account_* methods, which perform the actual operations.

Having method specific actions as parts of the chain (like having user react to a POST request to the account action) seems kind of counter-intuitive from a design standpoint. That might work, but I've never tried it.

The above examples are of course simplified. I usually have a base action in every controller setting the namespace and a common parent action, and all other actions in the controller will chain off that one. Then I'll have an object like above for loading single resources, and a root for a root action of the controller. Since you can build any kind of tree structure, it is rather flexible. So the best solution is often depending on what your constraints are.

like image 112
phaylon Avatar answered Sep 18 '22 13:09

phaylon