Laravel 5 redirect()->back() doesn't work as expected
Solution 1
If no HTTP_REFERER
is set, Laravel tries to get the last request from the session. This URL is stored in Illuminate\Session\Middleware\StartSession
:
protected function storeCurrentUrl(Request $request, $session)
{
if ($request->method() === 'GET' && $request->route() && ! $request->ajax())
{
$session->setPreviousUrl($request->fullUrl());
}
}
As you can see there are a few conditions, which unfortunately your JS requests fulfill:
- GET method
- Handled by a route
- Not an ajax request
Solution
As I see it, you have two options.
Disable the middleware
Warning: I haven't tested this and there could be side-effects
Go to app/Http/Kernel.php
and remove 'Illuminate\Session\Middleware\StartSession',
from the $middleware
. Now if no HTTP_REFERER
is set, Laravel will simply redirect to the root of your application (/
)
Extend the middleware and add additional constraints
You could extend the middleware and add some additional checking:
class CustomStartSession extends \Illuminate\Session\Middleware\StartSession {
public function storeCurrentUrl(Request $request, $session){
if(!ends_with($request->url(), '.js')){
parent::storeCurrentUrl($request, $session);
}
}
}
Then enable it by replacing the original StartSession
in app/Http/Kernel.php
with yours and any request with an URL ending in .js
will not be saved as "previous url"
Update
Apparently the middleware needs to be registered as a singleton to be fully functional. You can do that by adding this in the register
method of one of your service providers (e.g. AppServiceProvider
)
$this->app->singleton('App\Http\Middleware\CustomStartSession');
Solution 2
My current work-around is to extend the BaseController
like this:
<?php namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
abstract class Controller extends BaseController
{
use DispatchesCommands, ValidatesRequests;
/**
* The fallback url for redirects...
* Prevent faulty redirect if 'HTTP_REFERER' not set.
*
* @var string
*/
private static $back_url = '';
/**
* @param string $back_url The fallback url for redirects...
*/
public static function setBackUrl($back_url)
{
self::$back_url = $back_url;
}
/**
* @return string
*/
protected static function getBackUrl()
{
return self::$back_url;
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public static function redirect_back()
{
$back = self::getBackUrl();
if (!empty($_SERVER['HTTP_REFERER']))
{
return redirect()->back();
} else if (!empty($back)) {
return redirect()->to($back);
} else {
return redirect()->to('/');
}
}
}
And then call it like this:
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
/**
* Class MyController
*
* @package App\Http\Controllers
*/
class MyController extends Controller
{
/**
* Check permission for specified '$id'.
*
* @param int|string $id The datarow id
* @return boolean
*/
public function checkPermission($id)
{...}
/**
* Display a listing of the resource.
*
* @param int|string $id The datarow id
* @return Response
*/
public function index($id)
{
if (!$this->checkPermission($id)) {
self::setBackUrl(\URL::route('my_prefered_fallback_route'));
return self::redirect_back()
->withErrors(["message"]);
}
return 'Hello, API';
}
}
Solution 3
I know this is uggly but you can avoid storeCurrentUrl() by changing request method to POST in your Controller.
$request->setMethod('POST');
algorhythm
Updated on June 04, 2022Comments
-
algorhythm almost 2 years
I've got a problem with
redirect()->back()
when there is noHTTP_REFERER
set.Normally when I open my URL with an ID as parameter to show a specific datarow:
http://my-domain/module/ID
If the specified
ID
is not existent or the current loggedin user has no permission to open it, I makeif (!$this->checkPermission($id)) { return redirect()->back()->withError('message'); } // do my stuff to show the datarow
But when I change the browsers address field and load an non-permitted
ID
then there is noHTTP_REFERER
set andredirect()->back()
redirects me to my last loaded JavaScriptfor_this_view.js
(and shows it) added in the view as following:app.blade.php
<!DOCTYPE html> <html lang="de"> <head> @section('head') <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> {!! HTML::style('assets/jquery-ui/jquery-ui.min.css?ver=1.11.4') !!} <!-- some more scripts --> @show </head> <body> @yield('content') </body> </html>
show.blade.php
@extends('app') @section('head') @parent {!! HTML::script('for_this_view.js') !!} @stop @section('content') <!-- display datarow --> @stop
What is
redirect()->back()
doing here? If there is no URL to redirect back laravel is guessing one? And why? Maybe to ever have a way to redirect.That make me not able to use this function.
UPDATE
Ah, maybe I found my problem...
I've tried to dynamically load JavaScript's if they exist in a specific folder. I do that, because I want my JavaScript's be parsed like a view to get Blade Syntax in it. I added a specific folder to the
/config/view.php
and made files likefor_this_view_js.blade.php
. They are dynamically added asfor_this_view.js
in theroutes.php
.And therefore I made something like:
/* load view javascripts */ /** @var \SplFileInfo $path */ foreach (new RecursiveIteratorIterator( new RecursiveDirectoryIterator(public_path()) ) as $path) { if ($path->isFile() && ends_with($path->getFilename(), 'js.blade.php')) { Route::get('url_to_javascript.js', function() { $contents = View::make('for_this_view_js(.blade.php)'); $response = Response::make($contents); $response->header('Content-Type', 'application/javascript'); return $response; }); } }