adding a REST route chained with another route

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

adding a REST route chained with another route

Guillaume ORIOL
Dear Zend Framework users,

In the multi-account application I am working on, a user may access its application either by:
http://<account>.app.domain.com/<module>/<controller>/<action>
or:
http://app.domain.com/<account>/<module>/<controller>/<action>

To do so, I wrote this bootstrap method:

     public function _initRoutes()
     {
         $front = Zend_Controller_Front::getInstance();
         $router = $front->getRouter();

         $defaultRoute = new Zend_Controller_Router_Route(
             ':account/:module/:controller/:action/*',
             array(
              'account'    => 'demo',
                 'module'     => 'default',
                 'controller' => 'index',
                 'action'     => 'index'
             ),
             array('account' => '([a-z0-9]+)')
         );
         $router->addRoute('default', $defaultRoute);

         $pathRoute = new Zend_Controller_Router_Route(
             ':module/:controller/:action/*',
             array(
                 'module'     => 'default',
              'controller' => 'index',
                 'action'     => 'index'
             )
         );
         $hostnameRoute = new Zend_Controller_Router_Route_Hostname(
             ':account.app.domain.com',
             array('account' => 'demo'),
             array('account' => '([a-z0-9]+)')
         );
         $router->addRoute('hostname', $hostnameRoute->chain($pathRoute));
     }

Everything was ok, until I wanted to add a REST route for a whole module named "rest".
Adding the following at the end of the function consume the :account parameter that is never
obtained by the first route :

         $restRoute = new Zend_Rest_Route(
             $front,
             array(),
             array('rest')
         );
         $accountRoute = new Zend_Controller_Router_Route(
             ':account',
             array('account' => 'demo'),
             array('account' => '([a-z0-9]+)')
         );
         $router->addRoute('default', $accountRoute->chain($restRoute));

What is the solution?
--
Guillaume ORIOL
Software engineer
Technema
Reply | Threaded
Open this post in threaded view
|

Re: adding a REST route chained with another route

Wil Moore III
Since routes are matched in reverse order you will want your least priority routes to be defined first.
In other words, move your "default" route to the top.

--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: adding a REST route chained with another route

Guillaume ORIOL
Le 10/02/11 11:00, Wil Moore III a écrit :

>
> Since routes are matched in reverse order you will want your least priority
> routes to be defined first.
> In other words, move your "default" route to the top.
> -----
> --
> Wil Moore III
>
> Why is Bottom-posting better than Top-posting:
> http://www.caliburn.nl/topposting.html
>
> DO NOT TOP-POST and DO trim your replies:
> http://linux.sgms-centre.com/misc/netiquette.php#toppost

Thank you for your reply Wil,

But if I move the 'default' route to the top, won't it match even for REST requests?
For instance:
http://app.domain.com/<account>/rest/<controller>/<action>
will match the 'default' route instead of the 'rest' one, I guess.

Let me explain my use case differently. With the current routes, I get the account properly,
whatever URL is used:
http://<account>.app.domain.com/<module>/<controller>/<action>
or:
http://app.domain.com/<account>/<module>/<controller>/<action>

Now, I need to configure the routes in order to set a specific module as REST.
How can I do so?
--
Guillaume
Reply | Threaded
Open this post in threaded view
|

Re: adding a REST route chained with another route

Wil Moore III
Guillaume ORIOL wrote
http://app.domain.com/<account>/<module>/<controller>/<action>

Now, I need to configure the routes in order to set a specific module as REST.
How can I do so?
The first thing you will want to check when attempting to match a rest route is whether or not you are trying the correct URI.

For example:

This Uri will likely not match via the rest route:
app.domain.com/<account>/<module>/<controller>/<action>

but based on what I see in your route configuration, the following should match:
app.domain.com/rest (indexAction)
app.domain.com/rest/id (getAction)
app.domain.com/rest/id/edit (editAction)
app.domain.com/rest/new (newAction)
...

If you want a dynamic <account> parameter, you'll likely have to chain the rest route with the hostname route. I don't believe there is a way for the rest route to match unless the first segment (where you currently have <account>) corresponds to a module directory.

On the other hand, the default route will look at the first segment, and if there is no module directory that matches, it will use the default module and use the first segment as the controller. I am not sure if the rest router does this as well (I suspect it does but I haven't tested it), but if it does, you should be able to put your rest controller under your default module directory to get it to work. If you are having trouble figuring out how to test this, post back on what you are having trouble with.

BTW, even if the above works, I'm not sure you'll be able to get the name of the module as it may be replaced by the name of the default module. Again, I'm not sure so you'll have to test for that.

If none of this works, you may have better luck writing the rest routes from scratch (means you'll have to manually test for the correct HTTP method).

--
Wil Moore III

Best Practices for Working with Open-Source Developers
http://www.faqs.org/docs/artu/ch19s02.html

Why is Bottom-posting better than Top-posting:
http://www.caliburn.nl/topposting.html

DO NOT TOP-POST and DO trim your replies:
http://linux.sgms-centre.com/misc/netiquette.php#toppost
Reply | Threaded
Open this post in threaded view
|

Re: adding a REST route chained with another route

Guillaume ORIOL
Le 12/02/11 12:24, Wil Moore III a écrit :

> The first thing you will want to check when attempting to match a rest route
> is whether or not you are trying the correct URI.
>
> For example:
>
> This Uri will likely not match via the rest route:
> app.domain.com/<account>/<module>/<controller>/<action>
>
> but based on what I see in your route configuration, the following should
> match:
> app.domain.com/rest (indexAction)
> app.domain.com/rest/id (getAction)
> app.domain.com/rest/id/edit (editAction)
> app.domain.com/rest/new (newAction)
> ...
>
> If you want a dynamic<account>  parameter, you'll likely have to chain the
> rest route with the hostname route. I don't believe there is a way for the
> rest route to match unless the first segment (where you currently have
> <account>) corresponds to a module directory.
>
> On the other hand, the default route will look at the first segment, and if
> there is no module directory that matches, it will use the default module
> and use the first segment as the controller. I am not sure if the rest
> router does this as well (I suspect it does but I haven't tested it), but if
> it does, you should be able to put your rest controller under your default
> module directory to get it to work. If you are having trouble figuring out
> how to test this, post back on what you are having trouble with.
>
> BTW, even if the above works, I'm not sure you'll be able to get the name of
> the module as it may be replaced by the name of the default module. Again,
> I'm not sure so you'll have to test for that.
>
> If none of this works, you may have better luck writing the rest routes from
> scratch (means you'll have to manually test for the correct HTTP method).
> --
> Wil Moore III

Hi Wil and thank you for your extensive reply.

To simplify the problem, I temporarily removed the hostname route. But the issue remained.
Let me give you my router configuration:

     $defaultRoute = new Zend_Controller_Router_Route(
         ':account/:module/:controller/:action/*',
         array(
             'account'    => 'demo',
             'module'     => 'default',
             'controller' => 'index',
             'action'     => 'index'
         ),
         array('account' => '[a-z0-9]+')
     );
     $router->addRoute('default', $defaultRoute);

     $restRoute = new Zend_Rest_Route($front, array(), array('rest'));
     $accountRoute = new Zend_Controller_Router_Route(
         ':account',
         array('account' => 'demo'),
         array('account' => '[a-z0-9]+')
     );
     $router->addRoute('rest', $accountRoute->chain($restRoute));

And consider the following URL:
http://app.domain.com/customer1/default/invoice/index

The 'rest' route will be selected first.
'customer1' will properly be identified by the $accountRoute of the chain as the account
and 'default' will not match against the value 'rest' of the $restRoute. Up to now, everything is fine.

Then, the 'default' route will be selected. But only the subpath 'default/invoice/index' will be
passed to it *as the chain consumes the matched part of its child routes* (when they match) and
never restore them if a subsequent route doesn't match. Hence the parameters identification will be
erroneous.

Won't you consider this a bug? Am I missing something?
--
Guillaume
Reply | Threaded
Open this post in threaded view
|

Re: adding a REST route chained with another route

nebiros
Hey,

I was reading a devzone post: http://devzone.zend.com/article/12381 and somebody post a comment about how to prepend something to the default routes, in your case the account, in my case the region, so I wrote this in my application.ini file:

resources.router.routes.module.type = "Zend_Controller_Router_Route_Module"
resources.router.routes.region.type = "Zend_Controller_Router_Route"
resources.router.routes.region.route = ":region"
resources.router.routes.region.reqs.region = "[a-z]{2}"
resources.router.routes.region.abstract = 1
resources.router.routes.default.type = "Zend_Controller_Router_Route_Chain"
resources.router.routes.default.chain = "region, module"

So all urls look this way: /:region/:module/:controller/:action

Maybe this can help you.
Reply | Threaded
Open this post in threaded view
|

Re: adding a REST route chained with another route

Guillaume ORIOL
Le 02/03/11 21:39, nebiros a écrit :

> Hey,
>
> I was reading a devzone post: http://devzone.zend.com/article/12381 and
> somebody post a comment about how to prepend something to the default
> routes, in your case the account, in my case the region, so I wrote this in
> my application.ini file:
>
> resources.router.routes.module.type = "Zend_Controller_Router_Route_Module"
> resources.router.routes.region.type = "Zend_Controller_Router_Route"
> resources.router.routes.region.route = ":region"
> resources.router.routes.region.reqs.region = "[a-z]{2}"
> resources.router.routes.region.abstract = 1
> resources.router.routes.default.type = "Zend_Controller_Router_Route_Chain"
> resources.router.routes.default.chain = "region, module"
>
> So all urls look this way: /:region/:module/:controller/:action
>
> Maybe this can help you.
>
> --
> View this message in context: http://zend-framework-community.634137.n4.nabble.com/adding-a-REST-route-chained-with-another-route-tp3297415p3332254.html
> Sent from the Zend Framework mailing list archive at Nabble.com.

Hi nebiros,

Correct me if I am wrong but your configuration file shows how to chain a
Zend_Controller_Router_Route to a Zend_Controller_Router_Route_Module.

The problem I describe is that the :region variable will be removed from the request if you have two
subsequent routes to test and the first one doesn't match.

Regards
--
Guillaume

coa
Reply | Threaded
Open this post in threaded view
|

Re: adding a REST route chained with another route

coa
I think your problem could be caused by http://framework.zend.com/issues/browse/ZF-11121
I had a very similar problem, and posted a suggested fix there.

/christian