I am setting endpoints for my web application like this:
$router = new League\Route\RouteCollection;
function user_action (Request $request, Response $response) {
// some logic
.
.
.
return $response;
}
$router->addRoute('GET', '/user', 'user_action');
/user endpoint works well.
However when I use /user/ (extra slash in the end) I get a
League\Route\Http\Exception\NotFoundException
I want both endpoints to point to same function.
I can achieve the desired behavior by adding routes for both endpoints separately:
$router->addRoute('GET', '/user', 'user_action');
$router->addRoute('GET', '/user/', 'user_action');
What is the recommended way to resolve this?
There are two options
You can use htaccess to strip the trailing slash from the url.
Send the dispatcher the url without the trailing slash.
Solution 1: htaccess
Add the following rewrite rule to your .htacces:
RewriteRule ^(.*)/$ /$1 [L,R=301]
Solution 2: dispatcher
After you've created the router from a RouteCollection, you get the dispatcher and dispatch the routes:
$router = new RouteCollection();
$dispatcher = $router->getDispatcher();
$response = $dispatcher->dispatch($request->getMethod(), $request->getPathInfo());
The url which is send to the dispatcher can easily be adjusted:
$url = $request->getPathInfo();
// if url ends with a slash, remove it
$url = rtrim($url, '/');
$response = $dispatcher->dispatch($request->getMethod(), $url);
Two easy solutions, which one you use depends on you.
Related
I'm using the Bramus PHP router for my application, but it only seems to work on the index route which is /. Here's the piece of code that won't work:
public function handle(): Response
{
$response = null;
$request = new Request($_REQUEST);
$uri = $request->getRequestUri();
$container = $this->configureContainer();
$renderer = new RendererFactory();
$router = $this->configureRouter();
$controllerFactory = new ControllerFactory();
$controller = $controllerFactory->create($request, $container, $renderer, $uri);
$router->get($uri, function () use ($uri, $controller, &$response) {
$response = $controller->get();
});
$router->run();
return $response;
}
So when I go to the homepage, it works fine and returns the response with the correct value. However, when I go to say /about-us, the $router->get() never fires at all. It doesn't execute the anonymous function inside. Even replacing the $uri parameter with a hardcoded string like $router->get('/about-us'...) doesn't make the anonymous function execute.
I confirmed that the ControllerFactory does in fact return the right controller, so if the $router->get() fires, the get() method is in there and the $response should not be null. But now I get an error saying $response is null because the $router->get() won't fire.
What's the mistake I'm missing here? How can the index route work perfectly fine, but the router won't accept another route?
Edit Did some digging and added a var_dump to the Bramus Router
I added a var_dump() in the handle function inside the package itself, and it always says that the result of $this->getCurrentUri() is /, and not the URI in the browser.
My .htaccess is in the root directory and I redirect all requests to /public/index.php. Maybe that's the culprit? But I don't know how to fix it. My .htaccess:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /public/index.php?path=$1 [NC,L,QSA]
Forgot to add $router->setBasePath('/');, and my folder structure and .htaccess config made the router go crazy.
I am trying to figure out the best approach when linking to static pages using a loosely followed MVC design pattern.
I begin by rewriting all requests to the index.php which handles all request and break them down the url into the controller, action and parameters. However if i don't want to follow this url structure and just want to visit a static page such as 'http://example.com/home/' without having to call some action how would i achieve this without getting a php error caused by my router/dispatcher trying to request a file that does not exist?
I thought about setting up some switch statement or a if statement as shown below that checks if the url is set to something then uses a custom defined controller and action, or i wasn't sure whether to take the static resources out of the MVC directory and have it seperate and link to it that way?
<?php
class Router
{
static public function parse($url, $request)
{
$url = trim($url);
if ($url == "/")
{
$request->controller = "tasks";
$request->action = "index";
$request->params = [];
}
else
{
$explode_url = explode('/', $url);
$explode_url = array_slice($explode_url, 2);
$request->controller = $explode_url[0];
$request->action = $explode_url[1];
$request->params = array_slice($explode_url, 2);
}
}
}
?>
This works, but i'd rather not have a huge router setup for many different static resources as it feels tacky and that i am just patching together code. Would putting static pages in its own directory outside of MVC and linking to them in the views be a valid option? i'm relatively new to MVC so any guidance would be great.
Your application shouldn't receive request it is not supposed to handle, you can solve this on a webserver level:
if you are using apache for example, you can setup in the .htaccess file that the request should be directed to your front controller (ex: index.php) only if the requested resource does not exist
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L]
I have the beginnings of an application that i've picked up from another developer who's chosen Klein as the routing framework. I more familiar with Slim but still for the life of me can't figure out why the following doesn't work:
$klein->respond('GET', '/?', function($request, $response) {
echo 'this works!'
});
$klein->respond('GET', '/[i:id]', function($request, $response) {
echo 'This returns 404 not found';
});
$klein->dispatch();
.htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . /index.php [L]
In my httpd.conf I have "AllowOverride All"
I'm sure this is pretty straight forward but for the life of me I can't figure out why the second route doesn't work.
Consider both route patterns.
'/?' route pattern matches scheme:host and schema:hostname/. / is optional.
/[i:id] route pattern matches scheme:host/id where id is an integer.
Terminating the request uri with a / (for example scheme:host/2/) for the later route pattern will not match unless the route pattern is updated to match this case.
In order to match this case, use /[i:id]/? for the route pattern.
I have a problem. I am using slim and I have route for my main page:
$app->get('/', function() use ($app) { ...
In one of my controllers I want to redirect to the main page, so I write
$app->response->redirect('/', 303);
But instead of redirection to the '/' route I'm getting redirected to the root of my local server which is http://localhost/
What am I doing wrong? How should I use redirect method?
Slim allows you to name routes, and then redirect back to them based upon this name, using urlFor(). In your example, change your route to:
$app->get('/', function() use ($app) { ... })->name("root");
and then your redirection becomes:
$app->response->redirect($app->urlFor('root'), 303);
See Route Helpers in the Slim documentation for more information.
From Slim3 docs
http://www.slimframework.com/docs/start/upgrade.html
$app->get('/', function ($req, $res, $args) {
return $res->withStatus(302)->withHeader('Location', 'your-new-uri');
});
Slim 3
$app->get('/', function ($req, $res, $args) {
$url = 'https://example.org';
return $res->withRedirect($url);
});
Reference: https://www.slimframework.com/docs/v3/objects/response.html#returning-a-redirect
give your '/' route a name,
$app = new \Slim\Slim();
$app->get('/', function () {
echo "root page";
})->name('root');
$app->get('/foo', function () use ($app) {
$app->redirect($app->urlFor('root') );
});
$app->run();
This should give you the correct url to redirect
http://docs.slimframework.com/routing/names/
http://docs.slimframework.com/routing/helpers/#redirect
//Set Default index or home page
$app->get('/', function() use ($app) {
$app->response->redirect('login.php');
});
Slim 4:
$response->withHeader('Location', '/redirect/to');
Or in place of fixed string:
use Slim\Routing\RouteContext;
$routeParser = RouteContext::fromRequest($request)->getRouteParser();
$url = $routeParser->urlFor('login');
return $response->withHeader('Location', $url);
Slim Documentation: http://www.slimframework.com/docs/v4/objects/response.html#returning-a-redirect
RouteContext: https://discourse.slimframework.com/t/redirect-to-another-route/3582
For Slim v3.x:
Use $response->withStatus(302)->withHeader('Location', $url); instead of $app->redirect();
In Slim v2.x one would use the helper function $app->redirect(); to trigger a redirect request. In Slim v3.x one can do the same with using the Response class like so (see the following example)[1].
Use pathFor() instead of urlFor():
urlFor() has been renamed pathFor() and can be found in the router object.
Also, pathFor() is base path aware[2].
Example:
$app->get('/', function ( $request, $response, $args ) use ( $app ) {
$url = $this->router->pathFor('loginRoute');
return $response->withStatus(302)->withHeader('Location', $url);
});
Note: additional parameters can be supplied by passing an associative array of parameter names and values as a second argument of pathFor() like: $this->router->pathFor('viewPost', ['id' => 1]);.
The router’s pathFor() method accepts two arguments:
The route name
Associative array of route pattern placeholders and replacement values[3]
References:
Changed Redirect
urlFor() is now pathFor() in the router
Route names
I think using ./ instead of / will work also.
I think I faced a similar problem, and the issue was with my .htaccess config file. It should actually be something like this:
RewriteEngine On
RewriteBase /api #if your web service is inside a subfolder of your app,
# you need to pre-append the relative path to that folder, hope this helps you!
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
I am setting up a simple routing system for my new custom MVC framework that I am making.
Currently my router class views the URL as such:
www.example.com/controller/controller_action/some/other/params
So, essentially...I've been reserving the first two segments of the URI for controller routing. However, what if I just want to run the following?
www.example.com/controller/some/other/params
...which would attempt to just run the default controller action and send the extra parameters to it?
Here is the simple router I'm using:
\* --- htaccess --- *\
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]
\* --- index.php --- *\
if (array_key_exists('rt',$_GET)) {
$path = $_GET['rt'];
$uri = explode('/',$this->path);
if(empty($uri[0])) {
$load->ctrl('home');
}
elseif(empty($uri[1])) {
$load->ctrl($uri[0]);
}
else {
$load->ctrl($uri[0],$uri[1]);
}
}
else {
$load->ctrl('index');
}
\* --- loader class --- *\
public function ctrl($ctrl,$action=null) {
$ctrl_name = 'Ctrl_'.ucfirst(strtolower($ctrl));
$ctrl_path = ABS_PATH . 'ctrl/' . strtolower($ctrl) . '.php';
if(file_exists($ctrl_path)) { require_once $ctrl_path;}
$ctrl = new $ctrl_name();
is_null($action) ? $action = "__default" : $action = strtolower($action);
$ctrl->$action();
}
How can I do this?
You could handle this within your controller. Typically, MVC frameworks will call a default method when the requested method isn't available. Simply overwrite this fallback-method to call your desired method and pass the parameter list in as parameters.
For instance, KohanaPHP has the __call($method, $params) method that is called when the requested method doesn't exist. You could handle the logic within this, or its functional equivalent in your MVC framework..
This would let you keep the logic internal to the controller itself rather than having it blasted out between various files.