Page 1 of 1

Front controller multiple requests

Posted: Sat Apr 21, 2007 4:36 pm
by alex.barylski
How do you use a front controller design to implement two or more requests in one actually HTTP request?

1) Do you use action controller routing
2) Do you use header redirects

I've used the latter in the past and the former a few times in the last while. Both have their difficulties from what I can tell.

Which do you prefer and why?

Cheers :)

Posted: Sat Apr 21, 2007 4:45 pm
by Chris Corbyn
It depends upon the request. Most of the time a forward is more sensible than a redirection because you don't have to contact the browser purely to get to another action, but if you've just processed POST data or something from a form then a redirect is better to avoid duplicating the result of the request. There's no one-or-the-other answer really.

Posted: Sat Apr 21, 2007 4:54 pm
by alex.barylski
Agreed. Thanks for the confirmation, thats all I was really looking for was a second opinion. :)

The problem I am experiencing is POST data persisting without a redirection. However I cannot say I have encountered a situation where a 'forward' (better term) is required or desired over a redirect. Can you give an example?

For instance, if creating a user, one might create the user, redirect (prevent duplicate posting) and handle the request normally. Having the front controller call the 'create user' action only to have that create user action forward to the main handler, doesn't make technical sense as the POST is persisted.

With a redirect this is solved.

Anyways, if you know of a specific example, that would help me a lot :)

Thanks :)

Posted: Sat Apr 21, 2007 5:37 pm
by Chris Corbyn
Hockey wrote:Agreed. Thanks for the confirmation, thats all I was really looking for was a second opinion. :)

The problem I am experiencing is POST data persisting without a redirection. However I cannot say I have encountered a situation where a 'forward' (better term) is required or desired over a redirect. Can you give an example?

For instance, if creating a user, one might create the user, redirect (prevent duplicate posting) and handle the request normally. Having the front controller call the 'create user' action only to have that create user action forward to the main handler, doesn't make technical sense as the POST is persisted.

With a redirect this is solved.

Anyways, if you know of a specific example, that would help me a lot :)

Thanks :)
Example:

User submits a contact form to your "processForm" action. You validate the data they sent but they forgot to fill out a form field. You then set some error messages and within the same request you just forward to the displayForm action again. No redirect is needed because you haven't processed the data yet. Asking the browser to redirect the user to the form again just seems a bit pointless if you can forward the action request yourself.

Posted: Sat Apr 21, 2007 5:38 pm
by alex.barylski
Just to add to this disscussion:

The reason I asked this question is because I am beginning to wonder if I have over-complicated my front controller (copying Zend, etc). Having read countless articles on design patterns and a few books to boot I sometimes feel that some developers are over zealous in implementing a design pattern exactly as stipulated in a book, etc.

For example, the front controller clearly derived from traditional event driven desktop style applications where a message loop is absolutely required. However I'm not sure if the exact implementation of a front controller is *really* nessecary in the world of web development.

I mean, Apache and mod_rewrite already do something of what a front controller does but most importantly, a message pump isn't really needed because of the very nature of the Web and it's REQUEST/RESPONSE model.

It doesn't often make sense (at least I have yet to witness something practical) to have a front controller implemented with the ability to handle multiple requests in a single HTTP request - the two simply don't jive. I considered this while implementing my 100th version but figured to go with in for the sake of flexibility. But I haven't needed it.

The example I gave d11 above, demonstrates a possible use in that one could use the message pump to first handle actions (Creating a user, etc) and then continuing as normal with display the FORM or thankyou note (without a refresh - saving bandwidth). This is I think why most developer implement the front controller with such funcitonality but as d11 noted, the redirect is nessecary after a CRUD operation anwyays otherwise F5 for your user could cause some troubles.

So, again, I ask, does anyone know of an actual 'practical' purpose to have multiple actions handled per HTTP request (despite IMHO going against the very grain of the technology).???

I'm thinking I might drop my loop implementation for a simple straightforward single action handler front controller.

Cheers :)

Posted: Sat Apr 21, 2007 5:39 pm
by alex.barylski
d11wtq wrote:
Hockey wrote:Agreed. Thanks for the confirmation, thats all I was really looking for was a second opinion. :)

The problem I am experiencing is POST data persisting without a redirection. However I cannot say I have encountered a situation where a 'forward' (better term) is required or desired over a redirect. Can you give an example?

For instance, if creating a user, one might create the user, redirect (prevent duplicate posting) and handle the request normally. Having the front controller call the 'create user' action only to have that create user action forward to the main handler, doesn't make technical sense as the POST is persisted.

With a redirect this is solved.

Anyways, if you know of a specific example, that would help me a lot :)

Thanks :)
Example:

User submits a contact form to your "processForm" action. You validate the data they sent but they forgot to fill out a form field. You then set some error messages and within the same request you just forward to the displayForm action again. No redirect is needed because you haven't processed the data yet. Asking the browser to redirect the user to the form again just seems a bit pointless if you can forward the action request yourself.
Touche. I'll have to think about that for a bit :)

Edit: Excellent point. Having put some thought into it, that now makes sense. Thanks for your time. :) I would have factored that out of my front controller and only later had to re-implement it. Saved me mucho time-oh amigo ;)

Posted: Sat Apr 21, 2007 5:46 pm
by Chris Corbyn
I agree that you don't have to build something following a documented pattern to every single source line. The very nature of design patterns has the ideaology that they are merely a guide/template to help you tackle common problems aster but you will commonly customize code to your own specific needs.

A front controller is a very loose term anyway. There's no set-in-stone approach to it (some people even do them almost entirely procedurally). They are useful though and I disagree with the comment about things like this being the creation of overzealous developers. Using a front controller makes it easy to centralize logic that would otherwise need to be manually drawn into individual page requests. Things like Routing (without mod_rewrite "index.php/some/fake/path/here"), autoloading, response handling, database access etc become easier to provide to each request. I'm not saying you need a front controller to do all that, but it's a heck of a lot easier to maintain.

EDIT | In fact I have my example in code straight from the SwiftMailer website :P

Code: Select all

/**
   * Executes the send action
   */
  public function executeSend()
  {
    try {
      $swift = SwiftFactory::getInstance();
      $message = new Swift_Message("[SWIFTMAILER] " . $this->getRequestParameter("subject"));
      $sender = new Swift_Address(trim($this->getRequestParameter("email")), $this->getRequestParameter("name"));
      $message->setReplyTo($sender);
      $filename = "No file uploaded";
      if ($this->fileUploaded)
      {
        $filename = $this->getRequest()->getFileName("attachment");
        $this->getRequest()->moveFile("attachment", sfConfig::get("sf_upload_dir") . "/" . $filename);
        $message->attach(new Swift_Message_Attachment(new Swift_File(sfConfig::get("sf_upload_dir") . "/" . $filename)));
      }
      $tpl = file_get_contents(sfConfig::get("app_mail_template"));
      $tpl = str_replace(array("{message}", "{sender}", "{date}", "{attachments}"),
        array($this->getRequestParameter("body"), $sender->build(), date("r"), $filename), $tpl);
      if ($this->fileUploaded)
      {
        $message->attach(new Swift_Message_Part($tpl));
      }
      else
      {
        $message->setBody($tpl);
      }
      $sent = $swift->send($message, sfConfig::get("app_mail_to"), sfConfig::get("app_mail_from"));
      @unlink(sfConfig::get("sf_upload_dir") . "/" . $filename);
      if ($sent)
      {
        $tpl = file_get_contents(sfConfig::get("app_autoreply_template"));
        $tpl = str_replace("{sender_name}", $sender->getName(), $tpl);
        $message = new Swift_Message("Thanks for contacting me regarding Swift Mailer", $tpl);
        $swift->send($message, $sender, sfConfig::get("app_mail_to"));
        $swift->disconnect();
        $this->redirect("contact/thanks");
      }
      else throw new Exception("Message not sent");
    } catch (Exception $e) {
      $this->getRequest()->setError("runtime", "The message could not be delivered.  " .
        "This may be a temporary problem with the server.  Please try later.");
      $this->forward("contact", "index");
    }
  }
  /**
   * Validates the send action
   */
  public function validateSend()
  {
    $failed = false;
    $name = $this->getRequestParameter("name");
    if (!$name)
    {
      $this->getRequest()->setError("name", "The <em>Name</em> field must be completed.");
      $failed = true;
    }
    $email = $this->getRequestParameter("email");
    if (!$email)
    {
      $this->getRequest()->setError("email", "The <em>Email</em> field must be completed.");
      $failed = true;
    }
    else
    {
      if (!strpos($email, "@"))
      {
        $this->getRequest()->setError("email", "The email address does not look valid.  Please check the address.");
        $failed = true;
      }
    }
    $subject = $this->getRequestParameter("subject");
    if (!$subject)
    {
      $this->getRequest()->setError("subject", "The <em>Subject</em> field must be completed.");
      $failed = true;
    }
    $body = $this->getRequestParameter("body");
    if (!$body)
    {
      $this->getRequest()->setError("body", "Please provide a message to send.");
      $failed = true;
    }
    $file = $this->getRequest()->getFileName("attachment");
    if (!empty($file))
    {
      switch ($this->getRequest()->getFileError("attachment"))
      {
        case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE:
          $this->getRequest()->setError("attachment", "The file exceeds the maximum size.");
          $failed = true;
          break;
        case UPLOAD_ERR_PARTIAL:
          $this->getRequest()->setError("attachment", "The upload did not finish. Please try again.");
          $failed = true;
          break;
        case UPLOAD_ERR_NO_FILE:
          $this->getRequest()->setError("attachment", "The file did not upload.");
          $failed = true;
          break;
        case UPLOAD_ERR_OK: default:
          $this->fileUploaded = true;
          break;
      }
    }
    return !$failed;
  }
  /**
   * Handles errors in the send action validation
   */
  public function handleErrorSend()
  {
    $this->forward("contact", "index");
  }
Errors are *forwarded* back to the index page (the form), a successful result is *redirected* to the thanks page.

Another example could be where you choose to display a 404 page. Redirecting to a 404 page would be a bad thing to do!

Posted: Sat Apr 21, 2007 6:00 pm
by alex.barylski
d11wtq wrote:I agree that you don't have to build something following a documented pattern to every single source line. The very nature of design patterns has the ideaology that they are merely a guide/template to help you tackle common problems aster but you will commonly customize code to your own specific needs.

A front controller is a very loose term anyway. There's no set-in-stone approach to it (some people even do them almost entirely procedurally). They are useful though and I disagree with the comment about things like this being the creation of overzealous developers. Using a front controller makes it easy to centralize logic that would otherwise need to be manually drawn into individual page requests. Things like Routing (without mod_rewrite "index.php/some/fake/path/here"), autoloading, response handling, database access etc become easier to provide to each request. I'm not saying you need a front controller to do all that, but it's a heck of a lot easier to maintain.
haha...your right. I jumped the gun, no excuses. Having spent half my coding life as a desktop developer and switching to web dev some 6 or years ago, I often find myself trying to find the similarities and differences to better understand. For whatever reason, the multiple events per HTTP request in a front controller (like Zend or my own) simply slipped my comprehention and I couldn't see it's practical use, but thats why I posted here. Two heads are always better than one, regardless of how bright one might be. :P

Front controller itself wasn't the problem (as it's clear to me how it's useful) it was only the idea of a message pump/loop that had me considering it's practicality. I've only recently started to perform both front and server side validation. Previously I always just validated on client side and forced security on the server (preg_replace). Maybe thats why I glanced over that detail.

In anycase, it's clear you have a valid point so now I will return to coding :)

Cheers and thanks again :)