Magento中的404页面

HTTP的404状态码表示访问的链接不存在,这个状态码可能是由HTTP服务器直接返回的,也可能是由程序控制返回的。首先看看HTTP服务器返回的404。

Thank you for reading this post, don't forget to subscribe!

在Magento跟目录下的.htaccess文件中有如下配置:

<IfModule mod_rewrite.c>
 
############################################
## enable rewrites
 
    Options +FollowSymLinks
    RewriteEngine on
############################################
## always send 404 on missing files in these folders
 
    RewriteCond %{REQUEST_URI} !^/(media|skin|js)/
 
############################################
## never rewrite for existing files, directories and links
 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l
 
############################################
## rewrite everything else to index.php
 
    RewriteRule .* index.php [L]
 
</IfModule>

根据配置可知,当访问media|skin|js目录中不存在的页面文件时,httpd(Apache)将返回自身404设置,一般都是一个白底黑字的说明。可以修改默认处理:

// httpd
ErrorDocument 404 404.html
ErrorDocument 403 403.html
 
// nginx
fastcgi_intercept_errors on;
error_page 404 404.html;
error_page 403 403.html;

 

针对Nginx的Magento配置,参考:http://www.vfeelit.com/175.html

除了http服务的默认404处理,Magento本身也会可能做出404响应,查看Mage:run()方法代码:

  public static function run($code = '', $type = 'store', $options = array())
    {
        try {
//             throw new Mage_Core_Model_Store_Exception('');
        } catch (Mage_Core_Model_Session_Exception $e) {
 
        } catch (Mage_Core_Model_Store_Exception $e) {
            require_once(self::getBaseDir() . DS . 'errors' . DS . '404.php');
            die();
        } catch (Exception $e) {
    }

 

当捕捉到Mage_Core_Model_Store_Exception异常时,会返回errors/404.php页面,在try中主动抛出这个异常(以上代码注释部分),看看结果:

实际上它直接把error目录下的404.php作为模板返回,具体细节就不在这里讨论了。不过比较感兴趣的是,这个异常是怎么被抛出的,在什么地方?

我们可以直接搜索Mage_Core_Model_Store_Exception类,最终是在App的getStore方法中找到:

public function getStore($id = null)
{
    if (!Mage::isInstalled() || $this->getUpdateMode()) {
        return $this->_getDefaultStore();
    }
 
    if ($id === true && $this->isSingleStoreMode()) {
        return $this->_store;
    }
 
    if (!isset($id) || ''===$id || $id === true) {
        $id = $this->_currentStore;
    }
    if ($id instanceof Mage_Core_Model_Store) {
        return $id;
    }
    if (!isset($id)) {
        $this->throwStoreException();
    }
 
    if (empty($this->_stores[$id])) {
        $store = Mage::getModel('core/store');
        /* @var $store Mage_Core_Model_Store */
        if (is_numeric($id)) {
            $store->load($id);
        } elseif (is_string($id)) {
            $store->load($id, 'code');
        }
 
        if (!$store->getCode()) {
            $this->throwStoreException();
        }
        $this->_stores[$store->getStoreId()] = $store;
        $this->_stores[$store->getCode()] = $store;
    }
    return $this->_stores[$id];
}
如果无法取得

 

如果无法取得店铺Id或者获取了Id但是无法根据Id获取店铺代码,这个异常就会被抛出,如果是这个情况那就是这个店铺根本不存在,访问的店铺都不存在自然要返回404了。不过由于Magento的逻辑,一般还不容易遇到这个页面返回。

接下来探讨Magento的第二种404,也是最常见的。可以参考http://blog.ifeeline.com/470.html了解Magento的路由过程。对于不存在frontName,控制器和action方法,一般都会路由到cms/index/noRoute(默认是这样,后台可配置),以下是default路由器的match方法,它展示了如何实现这个noRoute:

    public function match(Zend_Controller_Request_Http $request)
    {
        $noRoute        = explode('/', Mage::app()->getStore()->getConfig('web/default/no_route'));
/*       
   <default>
            <cms_home_page>home</cms_home_page>
            <cms_no_route>no-route</cms_no_route>
            <cms_no_cookies>enable-cookies</cms_no_cookies>
            <front>cms</front>
            <no_route>cms/index/noRoute</no_route>
            <show_cms_breadcrumbs>1</show_cms_breadcrumbs>
    </default>
    模块名被修改为了CMS 对应index控制器  和 noRoute方法,noRoute方法就是处理404返回的逻辑
*/
          
        $moduleName     = isset($noRoute[0]) ? $noRoute[0] : 'core';
        $controllerName = isset($noRoute[1]) ? $noRoute[1] : 'index';
        $actionName     = isset($noRoute[2]) ? $noRoute[2] : 'index';
  
        if (Mage::app()->getStore()->isAdmin()) {
            $adminFrontName = (string)Mage::getConfig()->getNode('admin/routers/adminhtml/args/frontName');
            if ($adminFrontName != $moduleName) {
                $moduleName     = 'core';
                $controllerName = 'index';
                $actionName     = 'noRoute';
                Mage::app()->setCurrentStore(Mage::app()->getDefaultStoreView());
            }
        }
  
        $request->setModuleName($moduleName)
            ->setControllerName($controllerName)
            ->setActionName($actionName);
  
        return true;
    }

 

从web/default/no_route配置(后台可配置)中获取cms/index/noRoute,对应填入模块名,控制器名和action名,很明显,如果没有找到就使用Mage_Cms_IndexController控制器的noRoute方法来处理:

public function noRouteAction($coreRoute = null)
{
    $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
    $this->getResponse()->setHeader('Status','404 File not found');
 
    $pageId = Mage::getStoreConfig(Mage_Cms_Helper_Page::XML_PATH_NO_ROUTE_PAGE);
    if (!Mage::helper('cms/page')->renderPage($this, $pageId)) {
        $this->_forward('defaultNoRoute');
    }
}

 

Mage_Cms_Helper_Page::XML_PATH_NO_ROUTE_PAGE的值为web/default/cms_no_route,一般配置应该为no-route,接着就渲染这个no-route页面。这里先去后台看看配置:

接着进入Cms的Page:

可以看到,这个404页面就是一个Cms Page,可以在这里进行随意修改定制。回到代码,万一没有为roRoute指定页面,那么就调用defaultNoRoute来处理:

public function defaultNoRouteAction()
{
    $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
    $this->getResponse()->setHeader('Status','404 File not found');
 
    $this->loadLayout();
    $this->renderLayout();
}

 

可看到,它直接渲染输出:

注意看,它还是输出了一句话,这个defaultNoRoute方法实际应用了cms.xml配置中的

<cms_index_noroute translate="label">
    <label>CMS No-Route Page</label>
</cms_index_noroute>
 
<cms_index_defaultnoroute>
    <remove name="right"/>
    <remove name="left"/>
     
    <reference name="root">
        <action method="setTemplate"><template>page/1column.phtml</template></action>
    </reference>
    <reference name="content">
        <block type="core/template" name="default_no_route" template="cms/default/no-route.phtml"/>
    </reference>
</cms_index_defaultnoroute>

 

cms_index_defaultnoroute的句柄配置,它应用了1column.phtml布局,然后在content中添加一个子块,这个子块对应cms/default/no-route.phtml模板,这个模板中只有“There was no 404 CMS page configured or found.”这段文本。

如果不想用Cms的页面,那么就可以自定义这个默认的模板获取一个友好的404页面输出。甚至,可以不使用Cms模块提供的noRroute方法,比如你希望使用一个自定义的模块来处理No Route页面以实现比较复杂的逻辑,那么可以在后台修改Default No-route URL的设置,这个设置会影响到Default路由器的赋值。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注