Normally when an exception is thrown, Error controller takes command and displays error page with regular common header and footer.
This behavior is not wanted in Ajax request. Because in case of error, whole html page is sent over. And in cases where I'm directly loading the content of http response in a div, this is even more unwanted.
Instead in case of Ajax request, I just want to receive 'the actual error' thrown by exception.
How can I do this?
I think, one dirty way could be: set a var in ajax request and process accordingly. Not a good solution.
if you use either the contextSwitch or ajaxContext action helpers to encode your error (possibly turning off autoJsonSerialization) you could just pass the errors back as JSON / XML objects.
http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.controller.actionhelpers.contextswitch
class Error_Controller extends Zend_Controller{
public function errorAction(){
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addActionContext($this->getRequest()->getActionName(),'json')
->initContext();
$errors = $this->_getParam('error_handler');
$this->view->exception = $errors->exception;
}
}
From there you have to either pass a format=json parameter which each AJAX request or setup a routing chain that automatically appends it.
For a 'slightly' more secure setup you could use ajaxContext as your helper and only requests that have the XMLHttpRequest header will be served json.
I played with both of the previous answers but kept running into problems. There are a few reasons I had trouble, one of them is that I wanted to be able to return raw HTML from my regular controller when things went well.
This is the solution I eventually came up with:
class ErrorController extends Zend_Controller_Action
{
public function init()
{
// Add the context to the error action
$this->_helper->contextSwitch()->addActionContext('error', 'json');
}
public function errorAction()
{
// Check if this is an Ajax request
if ($this->getRequest()->isXmlHttpRequest()) {
// Force to use the JSON context, which will disable the layout.
$this->_helper->contextSwitch()->initContext('json');
// Note: Using the 'json' parameter is required here unless you are
// passing '/format/json' as part of your URL.
}
// ... standard ErrorController code, cont'd ...
The code I use preserves error handling for non-Ajax requests, while keeping the 'displayExceptions' option intact. It is exactly like the regular error handler, in that the stack trace, and request params are also sent back when 'displayExceptions' is active within your application.ini file. There's a lot of flexibility as far as sending back the JSON data - you could create a custom class, use a view with the JSON view helper, etc.
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
if ($this->getRequest()->isXmlHttpRequest()) {
$this->_helper->contextSwitch()->initJsonContext();
$response = array('success' => false);
if ($this->getInvokeArg('displayExceptions') == true) {
// Add exception error message
$response['exception'] = $errors->exception->getMessage();
// Send stack trace
$response['trace'] = $errors->exception->getTrace();
// Send request params
$response['request'] = $this->getRequest()->getParams();
}
echo Zend_Json::encode($response);
return;
}
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()->setHttpResponseCode(404);
$this->view->message = 'Page not found';
break;
default:
// application error
$this->getResponse()->setHttpResponseCode(500);
$this->view->message = 'Application error';
break;
}
// conditionally display exceptions
if ($this->getInvokeArg('displayExceptions') == true) {
$this->view->exception = $errors->exception;
}
$this->view->request = $errors->request;
}
}
Related
I made a controller.php file to handle ajax requests, with $_POST parameters action and module
if (isset($_POST["action"])) {
$action = strtolower($_POST["action"]);
if ($action === "get_notification") {
// return session notification messages
//...
} elseif (isset($_POST["module"])) {
require_once("libraries/class.module.php");
$module = new Module;
$moduleName = strtolower($_POST["module"]);
// check if module-name is valid
if ($module->verify($moduleName)) {
// load $modulename class
require_once("libraries/class.".$moduleName.".php");
// factory used to create instance of $moduleName
require_once("libraries/class.factory.php");
// note: moduleName class extends Module class
$module = $Factory->create($moduleName);
// verify method of $moduleName verifies action/method parameter
// using method_exists and cross-checking against a permitted methods list
if ($module->verify($action)) {
$message = $module->$action();
echo json_encode($message);
} else {
// handle invalid requests
echo json_encode(["0", "Invalid request received"]);
}
} else {
// handle invalid requests
echo json_encode(["0", "Invalid request received"]);
}
} else {
// handle invalid requests
echo json_encode(["0", "Invalid request received"]);
}
} else {
// handle invalid requests
echo json_encode(["0", "No request received"]);
}
But I read this post on Stack Overflow which advises against using variable methods. Should I opt for a switch case that checks each $action and calls corresponding method. That will result in a lot more code which was the initial reason I opted for this variable method solution.
It looks like what you really need is a proper routing system. You could make it yourself (as described here). Or use an existing solution, like FastRoute.
As for actually using "variable methods", in general it is a bad idea. But, if those methods are at the beginning of your call-stack (executed from the bootstrap-file), they are a really practical option.
When it comes to security concerns, the only public method in your controller classes should be ones, that you expect to be called. Which mean that you can simply do:
if (method_exists($controller, $action)) {
P.S. You don't have to do strtolower() there, because in PHP the class methods are not case-sensitive (and neither are the class names themselves).
P.P.S. Look into using composer as PSR-4 autoloader
When I submit a form in Laravel, the following controller method handles it:
public function update($id)
{
//handle input
return View::make('generic.success', ["message" => 'Data submitted successfully!']);
}
This works fine. However, instead of returning a view like above I'd like to return a redirect, because when I return the view directly, reloading the page resubmits the form.
So I tried to do this:
public function update($id)
{
//handle input
return Redirect::to('/success', ['message' => 'Data submitted successfully!']);
}
In my routes file I defined the success route:
Route::get('success', 'NotificationsController#success');
And set up a notification controller to display the view:
class NotificationsController extends BaseController {
public function success($message)
{
return View::make('generic.success', ["message" => $message]);
}
When I run the above code, I get the following error from Laravel:
InvalidArgumentException
The HTTP status code "1" is not valid.
I have no idea what this is supposed to tell me, and neither does Google apparently.
Can someone shed some light on this issue?
P.S.
Incidentally, being new to Laravel, I've noticed so far that Laravel's error reporting is very user-unfriendly, in that instead of telling me I have an issue with my router, or controller, or permissions, it displays these generic errors with no humane explanation of their cause. Is there a better way to troubleshoot problems in Laravel than relying on this?
For example, in the above incident, the error report points to this line of code...
public function setStatusCode($code, $text = null)
{
$this->statusCode = $code = (int) $code;
if ($this->isInvalid()) {
throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
}
...which is completely useless, as all it does is show me the code that printed the error itself.
The second parameter of the redirector's to() method is the HTTP status code that will be returned by the response, not data that will be passed along. Passing data when redirecting to GET routes can be done either via the query string or the session. The recommended solution here is to pass data via the current session using the with() method which passes that data for the next request. So in your case this would be the approach needed:
public function update($id)
{
return Redirect::to('/success')->with('message', 'Data submitted successfully!');
}
Then in your success method you can have this:
public function success($message)
{
return View::make('generic.success', ["message" => Session::get('message')]);
}
When in doubt always try checking the documentation first. The solution to this is explicitly stated in the Laravel Response Redirects Documentation.
Thanks a lot -Bogdan I found in the documentation that you post answer to my problem. In my case the solution was redirect to an action in a controller, like this...
return
\Redirect::action(
'PqrController#solicitud',
array($id)
)
->with(
'message',
'¡El estado de la solicitud ha sido actualizado correctamente!'
)
;
I redirect to a method in a controller, with one parameter array($id) and I put too in the session a message using ->with('message','Mensaje')
when creating an XMLrequest in a php file having a code which goes something like this... I am using a MVC ( model-view-controller structure ) and this is a controller php file..
Controller_Institute extends Controller_Default{
function register(){
try {
$this->requireLogin();
switch($this->method){
case 'GET':
$content = $this->render('institute_registration_confirm');
break;
case 'POST':
$result = mysql_query("SELECT * FROM password WHERE pass='".mysql_real_escape_string($_POST['pass'])."'");
$num=mysql_num_rows($result);
if($num==2)
{
$content = $this->render('institute_registration');
}
else
{
$content = $this- >render("message",array('msg'=>'Your password is incorrect'));
}
break;
}
$institute = R::dispense('institute');
$institute- >import($_POST,'name,latitude,state,longitude,address,phone,year,url');
$id = R::store($institute);
}
catch(exception $e){
//If there was an error anywhere, go to the error page.
$content = $this->render('error',array('exception'=>$e));
}
$page = $this->render('default',array('content'=>$content));
return $page;
}
i am sending the ajax request from within the function ... so when the ajax sends back the request , it gets caught in the switch case... and then the response text becomes the function return value replacing the actual text... any idea how to prevent the xml response from getting into the switch case...? the institute_registration is the view file and i am including that file in my framework and then triggering the ajax function from within that file to check whether the password ( to enable registration form ) is correct or not...
Given the limited information and pseudo-code, I recommend setting up a stand-alone page called say... "ajax.php" that is stand alone and doesn't base it's return value on the request method. The pages that use AJAX will need to either POST or GET from this page depending.
If you determine whether or not regular output vs AJAX output is returned via request method, then you are limiting yourself in 2 ways. The first is you will not be able to do 1 or the other on your web pages (GET vs POST) instead of both. Also, the second, when it comes to the AJAX, you will not be able to run GET & POST AJAX requests, and yes, you can do both with AJAX: http://net.tutsplus.com/tutorials/javascript-ajax/5-ways-to-make-ajax-calls-with-jquery/
I am creating a custom MVC style framework from scratch and am at the point where I need to implement the code to control what happens on POST.
At the moment I have a main index.php which acts as a controller and passes data to other controllers such as:
profilecontroller.class.php
forumcontroller.class.php
At the moment I see two options as to where the POST controllers can go ..
First Approach
Firstly for site wide posts such as login that can occur on any page I would use something like this in the very first index.php to redirect all POST to a specific POST controller that then sends the data to a model to be processed:
if($_POST)
//post controller, works on specific form id's
Alternate Approach
The other option I see would be to build the POST identifier into the model construction sections but I don't think this would be very manageable/wise as they'd always be checked and resulting in more loaded code?
Are there any good/simple examples out there?
I'm creating my mvc to be as light as possible so that's my reason for going from scratch.
In a RESTful setup, you would normally have a controller for an object, say news, and then actions such as add, edit, delete etc.
Within your actions, you should then assert what HTTP method should be used to access the method, if one should be. For example:
<?php
class NewsController extends AbstractController {
public function save() {
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
header('HTTP/1.1 405 Method Not Allowed');
die('Please use POST.');
}
// carry on knowing we're working with a POST request
}
}
Creating a separate controller for POST requests would, as you say, quickly becoming unruly and unmanageable.
If you're looking for a way of handling requests for different HTTP methods within different controller actions, then maybe check out ToroPHP. It's a lightweight (single file) router, where you map a request to a class that's referred to as a handler, and then that handler has methods for different HTTP methods. A quick example:
<?php
require 'lib/torophp/toro.php';
require 'classes/handlers/HomeHandler.php';
$toro = new ToroApplication(array(
array('/', 'HomeHandler')
));
$toro->serve();
And then your HomeHandler would look as follows:
<?php
class HomeHandler {
public function get() {
echo 'Hello, world!';
}
public function post() {
echo 'Try performing a GET request for the home page, buddy.';
}
// and so on...
}
Hope that helps.
This is my default Controller :
<?php
Class Controller_Home{
public $Registery = null;
final public function __construct($Registery){ $this->Registery = $Registery; }
final public function Init($Method=null){
# Quelle action on fait ?
if($Method){
$Split = explode('_', $Method);
$MethodName = 'Action';
foreach($Split as $Splitted){
$MethodName.= '_'.ucfirst($Splitted);
}
if(method_exists($this, $MethodName)){
$this->$MethodName();
} else {
echo '404';
die;
}
} else {
$this->Action_Default();
}
}
final public function Action_Default(){
$this->Registery->Import('Library.Account');
var_dump($this->Registery->Account);
echo 'Default Home';
}
}
As you can see, once you are in Action_Default, you can do whatever you want based on $_GET, $_POST, whatever you want ...
So with this code :
website.com/home/bob/ will use function Action_Bob inside the controller Home (Home::Action_Bob) ... if you see $_POST just put inside Action_Bob this
public function Action_Bob(){
if($_POST){
$this->Action_Bob_Post();
}
// continue
}
Let's say I have a controller template with a before function like so...
public function before()
{
parent::before();
if ($this->request === Request::instance())
{
// its a main request, throw an exception or redirect
Request::instance()->redirect('/');
}
else
{
// ok
}
}
But let's say I don't want to redirect, I want to stop the Request flow, and do nothing.
Does an exception do this? Is there a simple way, like Request::die();?
EDIT:: I actually don't want to halt the Request flow, just prevent this controller from doing anything. It's likely that this controller was called from another controller, and I want to pass the control back to the calling controller.'
Thanks!
1.Use exceptions (not tested yet):
try
(
Request->instance()->execute();
}
catch (MyRequest_Exception $e)
{
// do what you want
}
echo Request->instance()->send_headers->response();
// somewhere in before()
if ($error)
{
throw new MyRequest_Exception($errortext);
}
Change action name:
$this->request->action('oblivion'); // redirects to an "oblivion" action that does nothing
You can set a class variable in before() say:
$this->execute = false;
Then in your action:
public function action_example()
{
if (!$this->execute) return;
// etc
}