在Zend框架中如何重定向到之前的页面?

29

我希望在登录页面的操作中添加重定向URL(作为查询),以便在登录后,用户可以访问之前浏览的页面。

起初我考虑使用Zend Session将每个页面的URL保存在变量中。但是,我在文档中读到它会增加额外负担。那么,有没有更好的方法来实现此目的?或者是否有其他方法可以使用Zend Session而没有额外负担?

11个回答

17

首先,您需要通过Zend_Controller_Request类获取重定向的原始URL:

$url = Zend_Controller_Front::getInstance()->getRequest()->getRequestUri();

或者简单地通过:

$url = $_SERVER['REQUEST_URI'];

然后,关键部分是通过用户请求传递它。我建议使用Zend_Session库,尽管使用POST参数也是合法的:

$session = new Zend_Session_Namespace('Your-Namespace');
$session->redirect = $_SERVER['REQUEST_URI'];
请注意,我们保留的地址包括基本路径。 为了在控制器类中将客户端重定向,请禁用'prependBase'选项以失去基本路径插入:
$this->_redirect($url, array('prependBase' => false));

3
还有一种方法可以序列化请求对象:$session->redirect = serialize($this->getRequest())。实际上,这是我现在使用的解决方案,已经使用了一年;) - Morteza Milani
序列化请求对象可以保留所有接收到的数据,如POST数据。 - Morteza Milani
@Morteza M. 你是在哪里或何时使用你的方法进行请求重构的?插件吗? - Julian
@Julian 随时可以!例如,当用户发布需要特权的数据时,我会序列化请求。在将用户重定向到登录页面并成功登录后,我会反序列化他保存的请求并进行处理。是的,你可以在插件中实现这个功能。 - Morteza Milani
@Morteza M. 谢谢。运行得非常好! - Julian

9

我发现实现这一点的简单方法就是在登录表单中添加一个隐藏字段。

现在,我不确定你的登录表单是通用的HTML元素还是实际上是Zend_Form的一个实例,但如果它是Zend_Form的一个实例,你可以简单地添加以下内容:

$this->addElement('hidden', 'return', array(
        'value' => Zend_Controller_Front::getInstance()->getRequest()->getRequestUri(),             
            ));

然后在我的认证脚本中,就像上面的注释一样,我有一个简单的重定向,使用传递的值将他们返回到相同的页面。

$this->_redirect($this->_request->getPost('return'));

显然,在这两个例子中,它们只是为了压缩代码而编写的,可能并不代表完成它的最佳方法。我代码中使用getRequest()的两种方法实际上并没有嵌入到redirectaddElement中,但出于示例目的,我将它们放在那里。
上面的答案显然也可以工作,除非您进行了大量页面重定向。我现在以这种方式运行的主要原因是,并非所有我的表单都在Zend_Form中运行,同时更改隐藏的return输入文本框的值以进行测试也很好用。

谢谢Mark和Jesta, 我更喜欢Jesta的答案,因为我正在处理大量的路由、转发、preDispatch函数等等!!!不管怎样,还是谢谢你们。我会等待更多的回答。 - Morteza Milani
这个解决方案的问题是数据丢失。场景:用户登录应用程序并加载表单。他开始填写表单。同时,会话过期,他需要重新登录。当他使用此解决方案提交表单时,他发布的数据被丢失。 - Morteza Milani

6

基本上和Jesta在他的答案中做的一样,但我在我的“MW_Form”类中添加了以下函数——这是我所有表单的超类——很容易从控制器中使用$form->trackReferrer($this->getRequest());来跟踪引荐。getReferrer()函数接受一个“默认”参数(如果用户禁用了REFERER头,或者没有引荐 - 你会想要一个默认的重定向位置)。

  /**
   * Adds a form element named "referrer" and sets its default value to either
   * the 'referrer' param from the request, or the HTTP_REFERER header.
   *
   * @param Zend_Controller_Request_Abstract $request 
   * @return MW_Form
   * @author Corey Frang
   */
  public function trackReferrer(Zend_Controller_Request_Abstract $request)
  {
    $this->addElement('hidden', 'referrer');
    $this->setDefault('referrer', 
      $request->getParam('referrer', 
        $request->getServer('HTTP_REFERER')));
        // HTTP_REFERER not HTTP_REFERRER - grrr HTTP spec misspellings

    // use no decorator for the actual form element
    $this->referrer->setDecorators(array()); 

    // use our custom "referrer" decorator to stick the hidden before the <dl>
    $decorators = $this->getDecorators();
    $this->clearDecorators();
    foreach ($decorators as $class=>$decorator)
    {
      if (substr($class,-5) == '_Form') {
        $this->addDecorator('Referrer');
        $added = true;
      }
      $this->addDecorator($decorator);
    }
    if (!$added) $this->addDecorator('Referrer');

    return $this;
  }

  /**
   * Returns the referrer field if it exists.
   *
   * @return string | false
   * @param mixed $default The value to return if referrer isn't set
   * @author Corey Frang
   **/
  public function getReferrer($default = false)
  {
    if (!isset($this->referrer)) return $default;
    $val = $this->referrer->getValue();
    if ($val) return $val;
    return $default;
  }

使用装饰器,可以获得额外的好处,即不会使用 zend_form 创建的 <dl> 中的任何行:

class MW_Form_Decorator_Referrer extends Zend_Form_Decorator_Abstract  {
  /**
   * Attaches the standard "ViewHelper" decorator for the 'referrer' element
   * prepended on the content
   *
   * @return void
   * @author Corey Frang
   **/
  public function render($content)
  {
    $form = $this->getElement();
    if ($form instanceOf MW_Form)
    {
      $referrer = $form->referrer;
      if ($referrer)
      {
        $decorator = new Zend_Form_Decorator_ViewHelper(array('placement'=>self::PREPEND));
        $decorator->setElement($referrer);
        return $decorator->render($content);
      }
    }
    return "Error - No Referrer Found".$content;
  }
}

示例用法(来自控制器):

$form = $description->getEditForm();
$form->trackReferrer($this->_request);
if ($this->_request->isPost())
{
  if ($form->process($this->_request->getPost()))
  {
    return $this->_redirect($form->getReferrer('/page'));
  }
}

Gnarf 看起来不错。但是 "trackReferrer" 函数应该放在哪个文件中?它是什么类型的类?它是助手还是其他什么? - sanders
它在我的My_Form类中,该类扩展自Zend_Form,并且我所有的表单都是从这个类继承而来的... - gnarf

3

我在插件中设置了一个授权的预调度钩子。如果(且仅当)用户需要登录时,我会将请求URI保存到会话中,在登录后重定向到该页面。除了重定向到登录表单时有一些开销外,没有其他额外的开销。但这种情况下开销不是问题。 :)

if(!$auth->hasIdentity()){
  $this->_insertLastUrlToSession();
  $this->redirect('/index/login');
} else {
  //no overhead
}

谢谢大家, 所以,在 _insertLastUrlToSession 帮助程序中,我将使用新的 Zend_Session_Namespace 而不是 Zend_Session::start(),对吗?谢谢。 - Morteza Milani

2

我看到这个问题已经有答案了,但是我也想提供我的解决方案,只是采用静态方法的不同方式来实现,类似于换汤不换药的处理。

class App_Helpers_LastVisited {
    /**
     * Example use:
     * App_Helpers_LastVisited::saveThis($this->_request->getRequestUri());
     */
    public static function saveThis($url) {
        $lastPg = new Zend_Session_Namespace('history');
        $lastPg->last = $url;
        //echo $lastPg->last;// results in /controller/action/param/foo
    }

    /**
     * I typically use redirect:
     * $this->_redirect(App_Helpers_LastVisited::getLastVisited());
     */
    public static function getLastVisited() {
        $lastPg = new Zend_Session_Namespace('history');
        if(!empty($lastPg->last)) {
            $path = $lastPg->last;
            $lastPg->unsetAll();
            return $path;
        }

        return ''; // Go back to index/index by default;
     }
}

这不是一直在运行,只是根据需要进行。

这是整个代码,这是我博客文章的一部分(http://hewmc.blogspot.com/2010/08/simple-way-to-store-last-visited-url-in.html)


谢谢。我读了你的博客文章和这个答案。你知道,在这个问题中接受的答案在应用程序中使用表单时非常简单,根本不使用会话。但是对于Web应用程序来说,使用会话是很常见的,因此添加另一个键/值对来存储先前的URL是没有意义的,你的答案很有帮助。 - Morteza Milani
谢谢您的回复,非常感谢。还有感谢您访问我的博客,我通常没有访客:) 。实际上,我已经更新了那篇博客文章,但只是我的态度更加包容其他用例,代码并没有改变。祝您拥有愉快的一天! - Edward Hew

1

这个Zend框架插件允许您保存当前和最后一个合格的URL,并过滤掉不需要的URL。欢迎使用并评论:

<?php

class Plugins_PageLog extends Zend_Controller_Plugin_Abstract
{
    public function preDispatch(Zend_Controller_Request_Abstract $request){
        $module = $request->getModuleName();
        $controller = $request->getControllerName();
        $action = $request->getActionName();
        $params=$request->getParams();

        // to grap urls that are in default module, not in auth controller, and not an error url 
        $controller2= Zend_Controller_Front::getInstance();
        if ($controller2->getDispatcher()->isDispatchable($request) 
            && ( $module == 'default' || $module == NULL )
            && $controller != 'auth'
            && ( !isset($params['error_handler']))
        ) {

            // init 2 session variables: the current and last qualified url
            if (!isset($_SESSION['redirect'])) $_SESSION['redirect'] = '';
            if (!isset($_SESSION['last_visited_url'])) $_SESSION['last_visited_url'] = '';

            // tempurl is to save current qualified url temporarily to ensure current and last qualified url will not be same
            if (!isset($tempUrl)) $tempUrl = '';
            if ($_SESSION['last_visited_url'] != $_SESSION['redirect']) {
                $tempUrl = $_SESSION['redirect'];
                $tempParams = $_SESSION['redirect_params'];
            }

            // save current qualified url
            $_SESSION['redirect']=$request->getRequestUri();
            $_SESSION['redirect_params'] = $params;

            // to ensure there are no duplicated urls due to browser refresh 
            if ($tempUrl != $_SESSION['redirect']){
                $_SESSION['last_visited_url'] = $tempUrl;
                $_SESSION['last_visited_url_params'] = $tempParams;
            }
        }

        //echo '<pre>';var_dump($_SESSION['last_visited_url']);echo '</pre>';
        //echo '<pre>';var_dump($_SESSION['redirect']);echo '</pre>';
    }
}

0
除了gnarfs的答案之外,我对其进行了修改以使其验证——对于那些喜欢这样做的人。
$this->addDecorator(array('WrapClose' => 'HtmlTag'), array('tag' => 'div', 'placement' => 'PREPEND', 'closeOnly' => true));
$this->addDecorator('Referrer'); 
$this->addDecorator(array('WrapOpen' => 'HtmlTag'), array('tag' => 'div', 'placement' => 'PREPEND', 'openOnly' => true));

0
您可以尝试使用HTTP_REFERRER头像这样:
// str_replace is the easiest way to get rid of domain - u can also preg_replace it   
return str_replace("http://".Zend_Controller_Front::getInstance()->getRequest()->getServer("HTTP_HOST"),"",Zend_Controller_Front::getInstance()->getRequest()->getServer("HTTP_REFERER"));  

它并不总是可用的。 - Morteza Milani

0

如果您不喜欢通过会话传递变量,可以尝试以安全的方式获取 $_SERVER['HTTP_REFERER'] 变量。基本上,它会检查您的引荐 URL 是否与您的服务器本地名称和方案(http/https)匹配。

class My_Tools
{   
    public static function doesUrlMatchServerHttpHost($url)
    {       
        $scheme = Zend_Controller_Front::getInstance()->getRequest()->getScheme();
        $httpHost = Zend_Controller_Front::getInstance()->getRequest()->getHttpHost();
        $needleUrl = $scheme.'://'.$httpHost.'/';
        if (strpos($url, $needleUrl) !== 0)
        {
            return false;
        }
        return true;
    }

    public static function safelyGetReferrerUrl($default)
    {
        if ( isset($_SERVER['HTTP_REFERER']) == false){
            return $default;
        }
        if (self::doesUrlMatchServerHttpHost($_SERVER['HTTP_REFERER']) == false){
            return $default;
        }
        return $_SERVER['HTTP_REFERER'];
    }
}

然后只需

$referrerUrl = My_Tools::safelyGetReferrerUrl('/');

默认情况下,您可以设置本地URI('/')


0

$this->_redirect($this->getRequest()->getServer('HTTP_REFERER'));


它不保存状态。而且HTTP_REFERER并不总是可用的。 - Morteza Milani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接