【问题标题】:Question about how redirects should work关于重定向应该如何工作的问题
【发布时间】:2011-04-23 00:26:28
【问题描述】:

所以我有一个网络应用程序,我正在使用一个表单,该表单需要在提交之前填充所有字段。如果您尝试在未填充字段的情况下提交应用程序,它会再次加载页面并显示错误。填写完所有字段并单击提交后,它会重定向到同一页面并显示从 flashdata 生成的消息。请参阅下面的简化示例。

欢迎控制器:

function show_view() 
{
  $this->load->view('form');
}

function process_form()
{
  // make the 'quality' field required
  $this->form_validation->set_rules('quality', 'Quality', 'required');

  if($this->form_validation->run() == FALSE) //if the fields are NOT filled in...
  {
    echo form_error('quality');
    $this->load->view('form'); //reload the page  
  }
  else  // if the fields are filled in...
  {
    // set success message in flashdata so it can be called when page is refreshed. 
    $this->session->set_flashdata('message', 'Your rating has been saved');
    redirect(welcome/show_view);
  }
}

现在为了说明我的问题,假设我在“主页”视图中并导航到“表单”视图。如果我填充“质量”字段并单击提交,我将被重定向回“表单”视图,并显示成功消息。如果我单击浏览器上的后退按钮,它会将我带回“主页”视图。一切都按预期进行

现在假设我在“主页”视图中并导航到“表单”视图。如果我单击提交按钮而不填充“质量”字段,则会再次重新加载“表单”视图并显示错误消息。如果我然后填充“质量”字段并单击提交,我将被重定向回“表单”视图并显示成功消息。问题是,如果我点击浏览器上的后退按钮,它现在会带我回到出现错误的表单页面,我必须再次点击后退按钮才能返回“主页”视图。

最佳编码实践是什么,这样如果用户提交有错误的表单,它将显示错误,如果他们修复错误并再次提交表单,它将显示成功消息,如果他们点击返回浏览器,它会将他们带回“主页”视图??

【问题讨论】:

  • 我很确定我的解决方案正是您使用 Codeigniter 寻找的解决方案,如果您有任何问题,请告诉我。

标签: php forms codeigniter


【解决方案1】:

问题在于您使用两个单独的函数来进行表单处理。表单验证类文档并没有很好地解释它,我花了一段时间才意识到它,但是如果有错误,form_validation->run() 返回 false,但如果它是一个 GET 请求,并且随后占form_error()、validation_errors()、set_value()等相关函数中的GET请求。

CI 的最佳实践(以及一般情况下)是这样做的:

class Welcome extends CI_Controller{

function home(){
    $this->load->view('home');
}

function form() 
{
    // make the 'quality' field required
    $this->form_validation->set_rules('quality', 'Quality', 'required');

    // If the fields are NOT filled in...
    // or if there isn't a POST! (check the Form_validation.php lib to confirm)
    if ( $this->form_validation->run() === FALSE) 
    {
         // This form_error() function actually doesn't do anything if there 
         // wasn't a form submission (on a GET request)
         echo form_error('quality');
         $this->load->view('form'); // load or reload the page
    }
    else  // if the fields are filled in...
    {
         // set success message in flashdata so it can be 
         // called when page is redirected. 
         $this->session->set_flashdata('message', 'Your rating has been saved');
         redirect('welcome/home','location', 303);
         exit;
    }

}

然后在视图中有action="welcome/form" 的形式

基本上所有的表单错误函数和所有与表单验证相关的东西都会检查表单验证器是否实际运行......这是表单帮助文件中 form_error 函数的示例

function form_error($field = '', $prefix = '', $suffix = '')
{
    if (FALSE === ($OBJ =& _get_validation_object()))
    {
        return '';
    }

    return $OBJ->error($field, $prefix, $suffix);
}

当它们不是 POST 时,它会正常显示,并且具有您正在寻找的自然页面流。

与问题无关,但对表单验证类感到困惑/值得注意...如果您在参数字段中使用 xss_clean、prep_url 等过滤器,它实际上会为您重新填充 $_POST 数组,因此您不必'真的不需要做任何额外的事情。

有时值得看看 CI 源的内部结构,其中有一些不完全显而易见的聪明东西。

【讨论】:

    【解决方案2】:

    我期望表单将我重定向到资源列表页面,其中资源是用户最初提交的实体。

    如果用户正在向资源添加新条目,他们希望在成功提交表单后在列表中看到该条目。

    如果表单在同一页面上,您仍应执行基于 位置 的重定向,而不是 http 刷新。此用例的状态代码是303 See Other

    via Wikipedia HTTP 303 Status Code

    根据您的情况,我会这样做。

    function home(){
      $this->load->view('home');
    }
    
    function show_view() 
    {
        $this->load->view('form');
    }
    
    function process_form()
    {
        // make the 'quality' field required
        $this->form_validation->set_rules('quality', 'Quality', 'required');
    
     if($this->form_validation->run() === FALSE) //if the fields are NOT filled in...
     {
        echo form_error('quality');
        $this->load->view('form'); //reload the page  
     }
     else  // if the fields are filled in...
     {
        // set success message in flashdata so it can be called when page is redirected. 
        $this->session->set_flashdata('message', 'Your rating has been saved');
        redirect('welcome/home','location', 303);
        exit;
     }
    
    }
    

    【讨论】:

      【解决方案3】:

      我使用 Symfony 有一段时间了,我认为 Symfony 的解决方案是最佳的。它的工作方式是你有路由(我认为 CI 也有路由:CI routings)并且在你的控制器中你可以在你的“创建”方法中做这样的事情:

      if the form is valid
        set flash notice message
        redirect to (your homepage or something else defined in your routing file)
      else
        set flash error message
        set the current view to form
      

      您的表单操作与“创建”操作是同一个控制器(只能通过 POST 请求访问)。您可以通过 GET 请求达到的形式是“新”方法。因此,如果您单击表单上的提交,您将转到 create 方法,但新方法会第一次生成您的表单。如果您的表单未通过验证,您会留下 Flash 错误消息。如果您单击返回,您将获得一个新表单,但如果该表单验证您的操作(方法)会重定向到您之前在上面的 if 语句中设置的自定义页面。

      此方法也适用于 ajax,如果请求是 XHTTP 请求,您可以检查您的操作,如果表单验证,您只需返回,您可以在 javascript 中处理事情。我认为这是处理表单的最佳方式,如果您的用户没有启用 javascript,他仍然可以使用“标准”方式。顺便说一句:“创建”和“新建”使用相同的模板(视图)。我希望我把事情说清楚了。

      【讨论】:

        【解决方案4】:

        告诉浏览器忽略/删除历史页面的唯一可靠方法是使用 javascript 命令:

        location.replace(url);
        

        这会进行客户端重定向,但会替换浏览器历史记录中的当前位置。或者根本不进入新页面(ajax 调用)。

        如果您在表单中使用 POST 作为方法,他们会收到一个错误,提示他们必须向服务器重新提交信息才能返回,这会吓跑大多数人使用返回按钮。

        您可以使用 nonce 模式来生成时间戳或其他唯一 ID,在创建表单时将其放入会话和 show_view 函数中的隐藏字段,然后在流程函数中检查它是否匹配,并且如果匹配,则将其从会话中删除。这样,如果他们尝试两次提交相同的表单(通过单击后退按钮),您可以通过查看不匹配来检测它并将它们重定向到无错误的 show_view 或主页或您想要的任何其他地方。

        您还需要确保您的过期和缓存控制标头强制 Web 浏览器每次都访问服务器,而不是仅使用其本地缓存。 http://www.web-caching.com/mnot_tutorial/notes.html#IMP-SERVER

        不管是好是坏,大多数网站都会忽略诸如此类的后退按钮问题

        【讨论】:

        • Codeigniter 中的所有表单提交都是通过邮寄方式提交的,为什么我没有看到重新提交信息错误?
        • 使用 firebug 或 fiddler 检查您的标题。确保您已过期且未设置缓存。您可能还想尝试几个浏览器。无论您在标头中放入什么内容,有些人都对缓存更加积极,尤其是在后台操作中。刚刚添加了缓存教程的链接
        【解决方案5】:

        DOM 窗口对象通过历史对象提供对浏览器历史的访问。它公开了有用的方法和属性,让您可以在用户历史记录中来回移动,以及——从 HTML5 开始——操作历史堆栈的内容。 https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history

        另一件事是使用 ajax 提交表单!并根据响应填充错误或成功消息...(XML,JSON ..)当然这不完全是您想要的,但它在用户体验方面有很多优势,这是您正在努力改进的。

        尽可能避免重定向,并让页面显示与重定向位置相同的内容。这样浏览器就不会将多次提交视为不同的页面。

        此外,新的 HTML5 javascript 具有历史状态更改或任何名称。因此,您可以在您的程序中定义网站应如何通过后退按钮处理页面更改,并且...很快您将看到许多后退按钮也集成到 Web GUI 中。

        【讨论】:

          【解决方案6】:

          执行您建议的最简单方法是使用 HTTPRequest (AJAX) 将表单值提交给您的 PHP 验证器,然后使用 JavaScript 显示错误。这样您就不必进行任何重定向,并且后退按钮仍会带您回家。

          如果你坚持使用重定向,我有一些想法,但没有一个是优雅的。也许其他人对此有想法?

          虽然我真的建议使用 AJAX 表单提交来解决这个问题。

          【讨论】:

          • 我也采用了 ajax 方式,但我也希望有一个备用的非 ajax 方式。
          • 嗯,我能想到的唯一方法是在您的主页上设置一个标志,设置后会将您重定向到“成功”表单页面。然后在表单验证成功后简单地重定向到设置了该标志的主页。这样,当您点击后退按钮时,您仍然会出现在主页上。然而,正如我所说,我不认为这个解决方案是一个优雅的解决方案。也许其他人有更好的主意......
          【解决方案7】:

          据我所知 - 并且可以找到 - 没有可靠的方法来捕获历史回溯事件,并且所有解决方案都是基于 Javascript 的,因此您不妨构建一个 AJAX 表单。 我想说唯一可能的非 JavaScript 解决方案要么是 iframe(没人喜欢),要么是 BraedenP 建议的。

          就个人而言,如果你真的想要这个功能,我会构建一个 AJAX 表单,但它不能让非 JavaScript 用户以这种方式工作(他们应该习惯于获得稍微不那么简化的体验)。

          在您的页面上添加面包屑路径或后退按钮始终是一种很好的做法,因此这也会有所帮助。

          【讨论】:

            猜你喜欢
            • 2019-07-05
            • 2018-04-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-11-29
            • 2016-09-30
            相关资源
            最近更新 更多