I use a controller to do some custom uri routing for pages, it's currently working great.
Here is a stripped down version of the controller which uses PHPTAL as it's template engine.
public function index()
{
$this->tal->display('index');
}
public function view($url)
{
$this->loadView($url);
}
private function loadView($url)
{
if (file_exists(ROOTPATH . 'webroot/' . $url . '/index.html'))
{
$this->tal->display($url . '/index');
}
else
{
show_404();
}
}
The problem
I recently noticed that the following error was appearing in my logs each time the page controller was accessed:
ERROR - 2013-02-06 10:58:23 --> 404 Page Not Found -->
I found this strange as the page displays as expected and there is definitely no 404 header and the network panel shoes no 404 status.
I finally narrowed this down to the show_404() helper method being called in the loadView() method. Removing that line stops the errors from appearing in the log file altogether.
This §show_404()§ should only be executed if the view file cannot be found, in which case it should show the 404 error page, which it does. However the logging part of the method appears to be being executed on every call to the page controller, regardless of whether the loadView() method is called or not.
Example
I access the index() view in the browser, everything appears to work fine, the correct view file is loaded, there are no errors. However a 404 error message is logged from the loadView() method. The loadView() method isn't even being called from the index() method, yet the existence of its output in the log files seems to indicate that it is being executed.
The default behaviour of show_404 is to insert a log entry when its called. There's a second optional parameter to disable it, so if you just want to show the 404 page without logging it call it like this:
show_404('', false);
The first parameter is the string that would be appended after the --> in the logs, so you can leave that empty too.
As for why the _loadView function might be get called:
If you are using urls without the index.php in them (empty $config['index_page']) then every static asset (image, js, css, etc.) that the webserver server can't find (depends on concrete rewrite rules) will be passed to the php script and probably generate a 404 error there.
Related
I wrote the corresponding controller through the CodeIgniter document, but the page always shows me 404. My question is why exactly the same URL can not be displayed, and why this framework can only show me 404 pages. I remember that ThinkPHP has a complete running link for error reporting, and I can't see the problem with only one 404.
this is my url
http://localhost/index.php/demo/test
this is my Demo.php code
`<?php
use CodeIgniter\Controller;
class Demo extends Controller
{
public function test()
{
echo 1;
}
}`
Demo. PHP files must be in the controller folder and there are no other directories, but CI reports 404 errors to me
Its because they dramatically changed routes, you need to define in \Config\Routes.php and check their wiki as I am in process of refactoring my scripts.
Add a route with this
$routes->get('pineapple','Demo::test');
ensure your server has mod_rewrite enabled and that you have .htacces file.
make sure that your base_url in app/Config/App.php is set with an ending slash on the url /
I have the following problem. Currently I work on a project where I should develop a new module. The problem is, that the main module just uses the onBootstrap function to validate the request (every) and if it not on the route of the main module, it returns a notfound 404 error page.
Now all my new routes on my new module just dont work cause onBootstrap() just kicks in before. Is there a way to check in the main module if the route just hit and if every module dont find routes to get the 404 error page?
Im pretty new to this framework D=.
example:
MainModule.php
class Module
{
...
onBootstrap()
{
$request = ...->getRequest();
if($request->isNotValid()) {
return new 404Response();
}
}
...
}
class SideModule
{
...
// Never triggered
public function indexAction()
{
print("Hello World");
}
...
}
You should depend on a 404 resolver later in the code, not during bootstrap of your first module. I'd suggest getting rid of the code that returns the 404 response in MainModule onBootstrap() method and instead depend on Laminas\Mvc\View\Http\RouteNotFoundStrategy that's automatically injected during Application bootstrap (see Laminas\Mvc\Application::bootstrap(), the part where defaultListeners are attached. ViewManager is one of the defaultListeners, and ViewManager on its behalf attaches HttpRouteNotFoundStrategy in the Laminas\Mvc\View\Http\ViewManager::bootstrap() method).
RouteNotFoundStrategy not only will set a 404 status code to your MvcEvent->getResponse() object, but will also render a not-found page which you can
customize any way you like. See https://docs.laminas.dev/laminas-mvc/services/ for more information.
I created customs Twig templates for http error display to keep the site design unified by extending my base layout. (I want to keep my navigation menu and display the error, unlike the regular error messages)
It's working as expected but for the 404.
In the navigation menu of my base layout, I have a lot of is_granted('SOME_ROLES') to display the availables sections of the site depending of user's rights. When a 404 is thrown, the navigation menu is displayed as if the user is disconnected : {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} being false.
After some searches, I found that the router is executed before the firewall. Since no route is found when a 404 is thrown, the firewall isn't executed and the rights aren't send to the template.
The only workaround I found (source from 2014) is to add at the very bottom of the routes.yaml file this route definition :
pageNotFound:
path: /{path}
defaults:
_controller: App\Exception\PageNotFound::pageNotFound
Since every other routes hasn't match, this one should be the not found.
The controller :
<?php
namespace App\Exception;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class PageNotFound
{
public function pageNotFound()
{
return (new NotFoundHttpException());
}
}
Because a controller is executed, the firewall is executed and the 404 error page is shown as I expected (hooray !).
My question is : Is there any proper way to fix that issue instead of that workaround?
We had a similar issue.
We wanted to have access to an authentication token in error pages.
In the scenario where part of the website is behind a firewall, say example.com/supersecretarea/, we wanted than unauthorized users get a 403 error code when accessing any url behind example.com/supersecretarea/, even in the event that the page doesn't exist. Symfony's behavior does not allow that and checks for a 404 (either because there is no route or because the route has parameter which didn't resolve, like example.com/supersecretarea/user/198 when the is no user 198).
What we ended up doing was to override the default router in Symfony (Symfony\Bundle\FrameworkBundle\Routing\Router) to modify its behavior:
public function matchRequest(Request $request): array
{
try {
return parent::matchRequest($request);
} catch (ResourceNotFoundException $e) {
// Ignore this next line for now
// $this->targetPathSavingStatus->disableSaveTargetPath();
return [
'_controller' => 'App\Controller\CatchAllController::catchAll',
'_route' => 'catch_all'
];
}
}
CatchAllController simply renders the 404 error page:
public function catchAll(): Response
{
return new Response(
$this->templating->render('bundles/TwigBundle/Exception/error404.html.twig'),
Response::HTTP_NOT_FOUND
);
}
What happens is that during the regular process of Symfony's router, if something should trigger a 404 error, we catch that exception within the matchRequest function. This function is supposed to return information about which controller action to run to render the page, so that's what we do: we tell the router that we want to render a 404 page (with a 404 code). All the security is handled in between matchRequest returning and catchAll being called, so firewalls get to trigger 403 errors, we have an authentication token, etc.
There is at least one functional issue to this approach (that we managed to fix for now). Symfony has an optional system that remembers the last page you tried to load, so that if you get redirected to the login page and successfully log in, you'll be redirected to that page you were trying to load initially. When the firewall throws an exception, this occurs:
// Symfony\Component\Security\Http\Firewall\ExceptionListener
protected function setTargetPath(Request $request)
{
// session isn't required when using HTTP basic authentication mechanism for example
if ($request->hasSession() && $request->isMethodSafe(false) && !$request->isXmlHttpRequest()) {
$this->saveTargetPath($request->getSession(), $this->providerKey, $request->getUri());
}
}
But now that we allow non-existing pages to trigger firewall redirections to the login page (say, example.com/registered_users_only/* redirects to the loading page, and an unauthenticated user clicks on example.com/registered_users_only/page_that_does_not_exist), we absolutely don't want to save that non-existing page as the new "TargetPath" to redirect to after a successful login, otherwise the user will see a seemingly random 404 error. We decided to extend the exception listener's setTargetPath, and defined a service that toggles whether a target path should be saved by the exception listener or not.
// Our extended ExceptionListener
protected function setTargetPath(Request $request): void
{
if ($this->targetPathSavingStatus->shouldSave()) {
parent::setTargetPath($request);
}
}
That's the purpose of the commented $this->targetPathSavingStatus->disableSaveTargetPath(); line from above: to turn the default-on status of whether to save target path on firewall exceptions to off when there's a 404 (the targetPathSavingStatus variables here point to a very simple service used only to store that piece of information).
This part of the solution is not very satisfactory. I'd like to find something better. It does seem to do the job for now though.
Of course if you have always_use_default_target_path to true, then there is no need for this particular fix.
EDIT:
To make Symfony use my versions of the Router and Exception listener, I added the following code in the process() method of Kernel.php:
public function process(ContainerBuilder $container)
{
// Use our own CatchAll router rather than the default one
$definition = $container->findDefinition('router.default');
$definition->setClass(CatchAllRouter::class);
// register the service that we use to alter the targetPath saving mechanic
$definition->addMethodCall('setTargetPathSavingStatus', [new Reference('App\Routing\TargetPathSavingStatus')]);
// Use our own ExceptionListener so that we can tell it not to use saveTargetPath
// after the CatchAll router intercepts a 404
$definition = $container->findDefinition('security.exception_listener');
$definition->setClass(FirewallExceptionListener::class);
// register the service that we use to alter the targetPath saving mechanic
$definition->addMethodCall('setTargetPathSavingStatus', [new Reference('App\Routing\TargetPathSavingStatus')]);
// ...
}
I have the following (basic) route set up in a CI-based web app:
$route['sms/resend/(:num)/(:any)'] = 'sms/resend/$1/$2';
The controller + 'resend' method:
class Sms extends CI_Controller {
public function resend($to, $message) {
// my code
}
}
Logically speaking, anything that doesn't fit the route should be directed to a 404 page instead of the resend() method within the sms controller. This isn't the case, however. The following URL, for example, isn't redirected correctly, it goes to the same controller+method:
http://myapp/sms/resend/uuuu/WhateverMessage
What could be the problem?
After a bit of digging, I've come to understand that CI's default routing does not get deactivated when a default route related to a specific controller/method pair is added. That being said, if a URL does not fit the route $route['sms/resend/(:num)/(:any)'] = 'sms/resend/$1/$2', then the same URL is run through CI's default routing mechanism as a fallback, so it still takes me to the resend method of the sms controller. To prevent this from happening, I needed to add another custom route that follows all others related to the sms resending, that redirects any other url to a different controller+method. If this controller doesn't exist, you get the default 404 page.
So the final /config/routes.php file:
$route['sms/resend/(:num)/(:any)'] = 'sms/resend/$1/$2';
$route['sms/checkoperator/(:num)'] = 'sms/checkoperator/$1';
$route['sms/(:any)'] = 'somewhereovertherainbow';
I think the rout file is just for rerouting. Your URL don't fits the routing Conditions so it don't gets rerouted! So it goes the normal way wich is the same (in this case!)
Something like this could work!
(! :num) /(:any) '] = error page (or not existing page)
So every request wich doesn't start with a number gets redirected to the error page!
The syntax might be wrong!
This would work great :
$route['sms/resend/[^0-9]/(:any)'] = 'errorpage';
You have to replace the error page by something ;)
When I enter a non existent url on my site it takes me to the 404 page that is specified by this in routes.php:
$route['404_override'] = 'page/not_found';
So the design of the page is as I have made it in the view not_found.php in the "page" directory.
However if I use this function to manually enforce a 404:
show_404();
It takes me to the default CodeIgniter 404 page with no style:
404 Page Not Found
The page you requested was not found.
How can I make it go to the same 404 page that I specified in the routes file?
All the answers here seem very outdated (as of version 2, at least) or very overkill. Here's a much simpler and manageable way, which only involves two steps:
In application/config/routes.php modify the field 404_override to point to some controller:
// Specify some controller of your choosing
$route['404_override'] = 'MyCustom404Ctrl';
Inside the controller specified in your routes.php, implement the index() method so it loads your custom error 404 view. You might also want to set some headers so the browser knows you're returning a 404 error:
// Inside application/controllers/MyCustom404Ctrl.php
class MyCustom404Ctrl extends CI_Controller {
public function __construct() {
parent::__construct();
}
public function index(){
$this->output->set_status_header('404');
// Make sure you actually have some view file named 404.php
$this->load->view('404');
}
}
Like the in-code comment mentioned, be sure there's a view file in application/views/404.php where you actually have your custom 404 page.
As a side note, some of the answers in this page suggest you modify stuff inside /system, which is a bad idea because when you update your version of CodeIgniter, you'll override your changes. The solution I'm suggesting doesn't mess with core files, which makes your application much more portable and maintainable, and update resistant.
Use the _remap() function. This allows you to do some very powerful stuff with 404 errors beyond what is normally built in - such as
Different 404 messages depending if the user is logged in or not!
Extra logging of 404 errors - you can now see what the referring person was to the 404, thus helping track down errors. This includes seeing if it was a bot (such as Google bot) - or if it was a logged in user, which user caused the 404 (so you can contact them for more information - or ban them if they are trying to guess admin routes or something)
Ignore certain 404 errors (such as a precomposed.png error for iPhone users).
Allow certain controllers to handle their own 404 errors different if you have a specific need (i.e. allow a blog controller to reroute to the latest blog for example)
Have all your controllers extend MY_Controller:
class MY_Controller extends CI_Controller
{
// Remap the 404 error functions
public function _remap($method, $params = array())
{
// Check if the requested route exists
if (method_exists($this, $method))
{
// Method exists - so just continue as normal
return call_user_func_array(array($this, $method), $params);
}
//*** If you reach here you have a 404 error - do whatever you want! ***//
// Set status header to a 404 for SEO
$this->output->set_status_header('404');
// ignore 404 errors for -precomposed.png errors to save my logs and
// stop baby jesus crying
if ( ! (strpos($_SERVER['REQUEST_URI'], '-precomposed.png')))
{
// This custom 404 log error records as much information as possible
// about the 404. This gives us alot of information to help fix it in
// the future. You can change this as required for your needs
log_message('error', '404: ***METHOD: '.var_export($method, TRUE).' ***PARAMS: '.var_export($params, TRUE).' ***SERVER: '.var_export($_SERVER, TRUE).' ***SESSION: '.var_export($this->session->all_userdata(), TRUE));
}
// Check if user is logged in or not
if ($this->ion_auth->logged_in())
{
// Show 404 page for logged in users
$this->load->view('404_logged_in');
}
else
{
// Show 404 page for people not logged in
$this->load->view('404_normal');
}
}
Then in routes.php set your 404's to your main controller, to a function that DOES NOT EXIST - i.e.
$route['404'] = "welcome/my_404";
$route['404_override'] = 'welcome/my_404';
but there is NO my_404() function in welcome - this means ALL your 404's will go through the _remap function - so you achieve DRY and have all your 404 logic in one spot.
Its up to you if you use show_404() or just redirect('my_404') in your logic. If you use show_404() - then just modify the Exceptions class to redirect
class MY_Exceptions extends CI_Exceptions
{
function show_404($page = '', $log_error = TRUE)
{
redirect('my_404');
}
}
To change behavior of show_404 you need to modify a file from CI core (system/Core/Common.php), which is not very practical for feature updates.
Instead of that, you could:
Redirect users to the same place controller/method as specified in $route['404_override'].
You could create a custom library to handle custom 404's and use it like $libInstance->show_404();
public function show_404() {
set_status_header(404);
return "404 - not found";
}
Or use views:
public function show_404($template) {
set_status_header(404);
if (ob_get_level() > $this->ob_level + 1)
{
ob_end_flush();
}
ob_start();
include(APPPATH.'views/'.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}
If you tried the user_guide you would see that is shows the following:
show_404('page' [, 'log_error']) The function expects the string
passed to it to be the file path to the page that isn't found. Note
that CodeIgniter automatically shows 404 messages if controllers are
not found.
So you need to pass the controller name as a first parameter. The second is for logging being enabled or disabled.
CodeIgniter comes with a default 404 error page But by using routing option of codeigniter you can easily show your custom 404 error page to your user.
To do this just go to route file of your codeigniter project and type below line
$route['404_override'] = 'error_404';
Here error_404 is a controller file to display the custom 404 error page or any other type of error page in your codeigniter project.
Create the error_404 file and copy this code into this file
class Error_404 extends CI_controller
{
public function index()
{
$this->output->set_status_header('404');
// Make sure you actually have some view file named 404.php
$this->load->view('Error_page');
}
}
Make Error_page view file in your CodeIgniter view folder and that's it, Type any wrong url and now you can see your custom 404 error page.
If you have any doubts then you can follow this tutorial which has step by step tutorial to create custom 404 error page in codeigniter
Change application/errors/error_404.php file to the page you want shown. That's the file that show_404() is showing...
*show_404('page' [, 'log_error'])
This function will display the 404 error message supplied to it using the following error template:
application/errors/error_404.php
The function expects the string passed to it to be the file path to the page that isn't found. Note that CodeIgniter automatically shows 404 messages if controllers are not found.
CodeIgniter automatically logs any show_404() calls. Setting the optional second parameter to FALSE will skip logging.*
You need to set below code in application/config/routes.php
$route['404_override'] = 'page/not_found';
After you create one controller in application/controller named "page.php"
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class page extends CI_Controller {
public function __construct(){
parent::__construct();
}
public function not_found() {
$this->load->view('not_found');
}
}
Then you can create your own design for 404 error page in application/view named "not_found.php"
<html>
<body>
<section>
<div>
<div align="center" style="margin-top:50px; margin-bottom:50px;">
<img src="<?php echo base_url();?>images/404.jpg" /> // will be your image path.
</div>
</div>
</section>
</body>
</html>
Or
<html>
<body>
<section>
<div>
<div align="center" style="margin:0px auto;">
<h1>OOPS!</h1>
<h3>Sorry, an error has occured, Requested page not found!</h3>
<h5 align="center">Back to Home</h5>
</div>
</div>
</section>
</body>
</html>