2009年7月28日星期二

RE: [fw-mvc] router vs pre-dispatch

Just to follow up, I took a quick peek at the front controller code to double check how it dispatches. As I thought, it instantiates a request object at the very beginning, then passes it to a series of methods, such as various plugin methods for routeStartup, preDispatch, etc. Then after all that, it calls the standard dispatcher to dispatch, which then instantiates a controller and so forth.
 
So there are many opportunities to modify the request object long before it goes to a particular controller. One of those opportunities is $this->_plugins->routeStartup($this->_request). Another of those opportunities is $this->_plugins->preDispatch($this->_request). So the question comes back to why one stage would be easier to work with the request object than the other? What advantages are there to using a plugin preDispatch over a plugin routeStartup, or vice-versa? In which use cases would one choose routing over manipulating the request object at a later stage?
 
 
--Seth
 


From: Atkins, Seth (RICH1:5278)
Sent: Tuesday, July 28, 2009 4:44 PM
To: Peter Warnock
Cc: fw-mvc@lists.zend.com
Subject: RE: [fw-mvc] router vs pre-dispatch

>>How would you know the request parameters to feed to your RouteFactory without routing?  Routing translates the REQUEST_URI to parameters.
That was my question. Is there a request object available at this point? And what info is in it at this point. The answer is actually appears to be, yes, there is a request object available before routing. See http://framework.zend.com/manual/en/zend.controller.plugins.html, method: "public function routeStartup(Zend_Controller_Request_Abstract $request)". The docs specifically state that this method called before the router, and apparently a request object is passed to it.
 
Even if not, another workaround could be to put enough info in a session namespace (assuming relevant info can be collected from the previous request), instantiate the session in the bootstrap, get the info needed, create routes, yada yada. Unless needed parameters are part of POST'ed data, those parameters would be in the URI in the form of http://site.com/module/controller/action/param1/value/param2/value, so it would seem easy to create routes to handle those parameters. All I need to know to create the route is info about the user, what he has permissions on, etc, in order to create meaningful routes. I don't actually have to know what the actual URI is in advance of creating the route.
 
This is the same with creating any ACL. I don't need to know what you want to access to create a dynamic ACL. All I need to know is who you are and what permissions you have. I can get all that from Zend_Auth and the database that stores your permissions, and then create the ACL that controls what you can and cannot access. I could do the same thing by creating routes that redirect you from things you don't have permission to access to an index page that you do have access to or something like that.
 
>>If you're in preDispatch, you've already instantiated the controller, so it is too late to modifying the request.  You would want to look at the _forward method.
 
That would be correct if I was defining preDispatch within my ActionController. Notice I said "plugin", and the plugin is instantiated before routing, before dispatching, etc. Plugin methods are called all throughout the routing and dispatching process. To be specific here, there are two ways to create a preDispatch method. One is tied to a controller like this, and I believe this is what you are thinking of:
 
class MyController extends Zend_Controller_Action
{
    function preDispatch
    {
        //do something
    }
}
 
The other is by creating a controller plugin like this:
 
class My_Controller_Plugin extends Zend_Controller_Plugin_Abstract
{
    function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        //do something
        $request->setModuleName('default');
        $request->setControllerName('bar');
        $request->setActionName('baz');
    }
}
 
It is absolutely wrong to say you can't modify the request with preDispatch because, well, my code is doing a rather good job of doing just that impossible feat. Virtually all code examples you can find on Zend_Acl show just this type of request modification to in essence direct you to a login controller if needed. Whether or not a particular controller is already instantiated or not, the above works just perfectly fine at modifying the request. Try it. As an aside, if you have preDispatch methods defined in both a plugin and a controller, you will notice the latter is called after the former. The former is applied to ALL incoming controller/action requests, the latter is only called with that specific controller is used.
 
If a preDispatch plugin was not allowed to modify the request, why would ZF pass the request object by design to any preDispatch method of every action controller plugin?
 
 
--Seth
 


From: Peter Warnock [mailto:petewarnock@gmail.com]
Sent: Tuesday, July 28, 2009 3:58 PM
To: Atkins, Seth (RICH1:5278)
Cc: fw-mvc@lists.zend.com
Subject: Re: [fw-mvc] router vs pre-dispatch

On Tue, Jul 28, 2009 at 12:24 PM, Seth Atkins <satkins@nortel.com> wrote:
 
1) I could create a RouteFactory class that feeds routes based on the request parameters. I believe I could even use Zend_Auth and Zend_Acl within that factory class when creating routes, and coupled with instantiating the session also have access to persistent session data when building routes. In essence it seems to me I can make routing decisions based on everything I would ever want to know about the request, authentication status, etc. So no access can result in a route created that routes to a login page. If the conditions for access to 'bar' are not met, I can route to an index landing page. If they are, I can simply not create a route at all and let it go through.
 

How would you know the request parameters to feed to your RouteFactory without routing?  Routing translates the REQUEST_URI to parameters.
 
or
 
2) I could in essence do all the same things in a preDispatch method of a controller plugin. But instead of creating routes, I'm simply altering the request object like this: $request->setControllerName('bar') based on essentially the same criteria. I could use the session data, Zend_Auth, and Zend_Acl to build the rules and then the result of those rules is altering the request object, or not.

If you're in preDispatch, you've already instantiated the controller, so it is too late to modifying the request.  You would want to look at the _forward method.
 
 
Or simply put, the basic algorithm of my ACL generator is identical, how I gather the input is a little different but after it all goes through the salad spinner, the last bit of code is like this:
 
if (!$acl->isAllowed($role, 'foo', $privilege)) {
    //create some routes for the router to
}
 
if (!$acl->isAllowed($role, 'foo', $privilege)) {
    // $request->setControllerName('bar')
    //        ->setActionName('index');
    // etc
}

If you want to match a route based on db values, you can extend the Route abstract.  Keep in mind the lookup will occur on every unmatched request, whereas ACL lookups might only be used on unauthenticated sessions.

- pw
 

没有评论: