views:

240

answers:

7

I have a controller and action which I'm accessing through a custom URL. The original route is still accessible though at the default location

zend.com/controller/action

How can I change this to simulate a "Page not found" when the user tries to access this URL? Is it possible?

+6  A: 

You could change the main controller script to redirect a certain controller name and action name to a new page. But it's probably easier to add a new rule to the .htaccess file, indicating that this specific URL should be redirected to an error page. Example:

RewriteRule ^controller/action/?$ / [R=404,L]

Or redirect the page to an error page within your site:

RewriteRule ^controller/action/?$ /error/page-not-found/ [L]
Alec
This sounds like a general solution. Are you talking about zend?
jblue
The `.htaccess` rewrite is a pretty general solution, but it works equally for sites written in Zend as well.
gnarf
+6  A: 

If the action handler is used to respond to both URLs, you would first have to detect which URL is being requested (using $this->_request->getRequestUri()). If the default URL is detected I think the easiest way to create a "page not found" would be to use

$this->_redirect("/path/to/simulated/404/page") 

and set up a controller and action to respond.

This won't actually send an HTTP 404, though. To do that, I think you would have to raise an exception within your action handler. I don't know what the official "zendy" way of doing this is, but this seems to work:

throw new Zend_Controller_Action_Exception('Not Found', 404);
bogeymin
It would be much easier to detect which route the router used instead of the request uri. - See my answer
gnarf
I would strongly recommend avoiding a redirect Throw an exception (a custom App_PageNotFoundException is better than using the Z_C_Action_Exception, or you can't tell the two apart) and then displaying the appropriate message in your error view.
David Caunt
+2  A: 

If you are looking other solutions than mod_rewrite based, you may create a Regex Route to match the actions you need to hide.

The other solution is to restrict access to Actions using Zend_Acl, treating each action as an ACL resource.

But the simplest and most lightweight solution is still mod_rewrite in .htaccess.

Edit:

As you can see, this may be done in numerous ways. But probably, you will need some kind of the switch, to still allow somehow to access the "hidden" action. In this case, use:

  • mod_rewrite for quick implementation (switching requires the person to know the .htaccess rules)
  • Zend_Router - the person who knows the right route can still access the feature
  • Zend_Acl + Zend_Auth for scalable and secure solution.

If you don't need to have authenticated users, Zend_Acl combined with Zend_Router might be the solution.

For smart handling the exceptions and building ACL's, see this (and other posts on this blog):

Handling errors in Zend Framework | CodeUtopia - The blog of Jani Hartikainen

takeshin
+3  A: 

You need to use:

$this->getResponse()->setHttpResponseCode(404);

And build your own 404 view

$this->view->message = 'Page not found';

Or you could forward to an error controller for example

$this->_forward('page-not-found', 'error');

Finally, if you have in your error controller

//...
switch ($errors->type) {
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:

                // 404 error -- controller or action not found
                $this->getResponse()->setHttpResponseCode(404);
                $this->view->message = 'Page not found';
                break;
//...

You can just do as @bogeymin said:

throw new Zend_Controller_Action_Exception('Not Found', 404);
Keyne
+3  A: 

By default the router includes default routes for :module/:controller/:action/ and :controller/:action/. You can disable these with:

$router->removeDefaultRoutes();

then only routes you setup will work. If you still want to use the default routes for some other things, you'll either have to go with one of the other answers posted or add your own 'default' routes which will match all but the modules/controllers you have custom routes for.

Tim Fountain
So how do I add my own default routes to match all but the controllers/actions I have custom routes for? Is there something like `matchAllExcept($custom_routes)`?
jblue
Unfortunately not, but you could add a regular expression route which matches only the modules you want - something like: (news|products|profiles)/:controller/:action
Tim Fountain
+2  A: 

If you don't want to remove the default route as @Tim Fountain suggests, you should do something like this in your controller (either preDispatch or whateverAction methods)

$router = $this->getFrontController()->getRouter();
$route = $router->getCurrentRouteName();
// if we reached this controller/action from the default route, 404
if ($route == 'default')
{
  throw new Zend_Controller_Action_Exception('Not Found', 404);
}
gnarf
A: 

I think, all answers above are incorrect. Those show ways to achieve the same thing, but present logic at the wrong place in your application, which eventually can cause trouble later on.

The correct part of your route logic is, how extremely simple, in the routes. What is missing is that the default route Zend_Controller_Router_Route_Module does not allow you to add exceptions to specific routes. So what you need to do, is remove the default route from your routes, and add a new custom route (which should function exactly as the default route, but allows excludes) at it's place.

You can write the new route by extending the class of the default route.

class My_Custom_Route extends Zend_Controller_Router_Route_Module
{
    protected $_excludes = array();

    public function exclude($abc)
    {
         //add to $_excludes here the controller/action you want to exclude
    }

    public function match($abc)
    {
         //add functionality here that denies if the mod/contr/action is in $_excludes
         //you can also add this in a separate method
         //re-use parent code
    }
}

You can now add add the excludes for example in a config file, and load + add the excludes at the place you initiate the new Route (and add it to the router). Off you go.