开发者

Question about how redirects should work

So I have a web app that i'm working on with a form that requires all of the fields to be populated before submitting. If you try to submit the app without a field being populated, it loads the page again with the errors. Once you fill all the fields in and click submit, it does a redirect to the same page and shows a message which is generated from flashdata. See simplified example below.

Welcome controller:

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() == FA开发者_Go百科LSE) //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);
  }
}

Now to illustrate my issue, lets say I'm on the 'home' view and I navigate to the 'form' view. If i populate the 'quality' field and click submit, i get redirected back to the 'form' view, with a success message. If i click the back button on the browser, it takes me back to the 'home' view. EVERYTHING WORKS AS EXPECTED

Now lets say i'm on the 'home' view and I navigate to the 'form' view. If i click the submit button without populating the 'quality' field, the 'form' view is reloaded again and it displays the error message. If i then populate the 'quality' field and click submit, i get redirected back to the 'form' view with a success message. The problem is, if i click the back button on the browser, it now takes me back to the form page with the error, and I have to click the back button again to get back to the 'home' view.

What is the best coding practice so that if a user submits the form with errors, it will display the errors, and if they fix the errors and submit the form again it will display the success message and if they click back on the browser, it will take them back to the 'home' view??


The problem is that you're using two separate functions to do form handling. The form validation class docs don't really explain it well, and it took me awhile to realize it but the form_validation->run() returns false if there is an error, but also if it is a GET request, and subsequently accounts for the GET request in the related functions like the form_error(), and validation_errors(), set_value(), etc.

The best practice in CI (and in general) is to do this:

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;
    }

}

then in the view have the form action="welcome/form"

Basically all of the form error functions and all the stuff related to form validation have checks to see if the form validator actually ran... here is an example from the form_error function in the form helper file

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

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

When their isn't a POST, it shows as normal, and has the natural page flow you are looking for.

Unrelated to the question, but confusing/noteworthy about the form validation class... if you use the filters like xss_clean, prep_url, etc. in the parameters field, it actually repopulates the $_POST array for you, so you don't really need to do anything extra.

Sometimes it's worth taking a look at the internals of the CI source, there's some clever stuff in there which isn't entirely obvious.


What I expect is for the form to redirect me to a resource listing page, where resource is the entity the user was submitting initially.

If the user is adding a new entry to a resource, they expect to see the entry in the listing once the form has been submitted successfully.

If the form is on the same page, you should still do a location based redirect instead of a http refresh. The status code for this use case is 303 See Other.

via Wikipedia HTTP 303 Status Code

Here is what I would do given your scenario.

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;
 }

}


I'm using Symfony for a while and I think that Symfony's solution is optimal. The way it works is that you have routings (I think that CI has routings too: CI routings) and in your controller you can do something like this in your "create" method:

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

Your form's action is the same controller with the "create" action (which can be only reached via a POST request). The form which you can reach with GET requests is the "new" method. So if you click submit on your form you go to the create method, but the new method generates your form for the first time. If your form does not validate you stay there with flash error messages. If you click back you get a new form, but if the form validates your action (method) redirects to a custom page you set before in the if statement above.

This method works with ajax too, you can check in your action if the request is an XHTTP one, you just return if the form validates or not and you can handle things in javascript. I think that this is the best way to handle forms and if your user does not have javascript enabled he can still use the "standard" way. And by the way: "create" and "new" uses the same template (view). I hope that i made things clear.


The only reliable way to tell the browser to ignore/remove pages in its history is with the javascript command:

location.replace(url);

this does a client-side redirect but replaces the current location in your browser history. Or alternatively not to going to a new page at all (ajax call).

If you use POST as the method in your form they will get an error saying they have to resubmit information to the server to go back, which will scare most people off from using the back button.

You could use a nonce pattern where you generate a timestamp or other unique id, put it in the session and a hidden field in your show_view function when you create the form and then check for a match it on your process function, and remove it from the session if there is a match. That way if they try to submit the same form twice (by clicking the back button) you can detect it by seeing there is not a match and redirect them to the errorless show_view or home page or wherever else you want.

You'll also want to make sure your expires and cache-control headers are forcing the web browser to hit the server everytime vs just using its local cache. http://www.web-caching.com/mnot_tutorial/notes.html#IMP-SERVER

For bettor or worse, most web sites ignore the back button issues such as these


The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack. https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history

Another thing would be to submit the form with ajax! and populate the errors or success message based on response...(XML,JSON..) of course this is not exactly what you were looking for but it has a lot of advantages in user experience which is what you are trying to improve.

If at all possible avoid a redirect and have the page show the same content as where its being redirected. That way browsers won't count multiple submissions as different pages.

Also the new HTML5 javascript has history state change or whatever its called. So you can define in your program how the site should behave to page changes through the back button and ... soon you will see a lot of back buttons integrated into the web GUI as well.


The easiest way to do what you're proposing is to submit the form values to your PHP validator using an HTTPRequest (AJAX) and then show the errors using JavaScript. This way you won't have to do any redirecting, and the back button will still take you home.

If you insist on using redirections, I have a few ideas in mind, but none of them are elegant. Perhaps someone else has an idea for this?

Although I really would recommend using AJAX form submission as a solution for this problem.


As far as I know - and can find - there is no reliable way to catch the history back event, and all the solutions are Javascript based so you might as well build an AJAX form. I'd say the only possible non-javascript solution would be either an iframe (which nobody likes), or what BraedenP suggests.

Personally, if you really want this functionality, I'd build an AJAX form and just not have it work this way for non-javascript users (they should be used to having a slightly less streamlined experience).

It's always good practice to add a breadcrumb trail or back button on your page, so this could help as well.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜