Laravel 5 redirect()->back() 没有按预期工作
Laravel 5 redirect()->back() doesn't work as expected
当没有设置 HTTP_REFERER
时,redirect()->back()
出现问题。
通常当我打开我的 URL 并使用 ID 作为参数显示特定数据行时:
http://my-domain/module/ID
如果指定的ID
不存在或者当前登录用户没有权限打开,我就
if (!$this->checkPermission($id)) {
return redirect()->back()->withError('message');
}
// do my stuff to show the datarow
但是当我更改浏览器地址字段并加载一个不允许的 ID
时,就没有 HTTP_REFERER
设置并且 redirect()->back()
将我重定向到我上次加载的 JavaScript for_this_view.js
(并显示)在视图中添加如下:
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
redirect()->back()
在这里做什么?
如果没有 URL 重定向回来 laravel 猜一个?为什么?也许永远有办法重定向。
这让我无法使用这个功能。
更新
啊,也许我发现了我的问题...
如果 JavaScript 存在于特定文件夹中,我会尝试动态加载它们。
我这样做是因为我希望我的 JavaScript 像视图一样被解析以在其中获取 Blade 语法。我在 /config/view.php
中添加了一个特定的文件夹,并制作了 for_this_view_js.blade.php
之类的文件。它们在 routes.php
.
中动态添加为 for_this_view.js
因此我做了类似的东西:
/* 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;
});
}
}
我目前的解决方法是像这样扩展 BaseController
:
<?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('/');
}
}
}
然后这样称呼它:
<?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';
}
}
如果未设置 HTTP_REFERER
,Laravel 会尝试从会话中获取最后一个请求。此 URL 存储在 Illuminate\Session\Middleware\StartSession
:
protected function storeCurrentUrl(Request $request, $session)
{
if ($request->method() === 'GET' && $request->route() && ! $request->ajax())
{
$session->setPreviousUrl($request->fullUrl());
}
}
如您所见,不幸的是您的 JS 请求满足了一些条件:
- GET 方法
- 由路由处理
- 不是 ajax 请求
解决方案
在我看来,你有两个选择。
禁用中间件
警告:我还没有测试过这个,可能会有副作用
转到 app/Http/Kernel.php
并从 $middleware
中删除 'Illuminate\Session\Middleware\StartSession',
。现在,如果没有设置 HTTP_REFERER
,Laravel 将简单地重定向到您的应用程序的根目录 (/
)
扩展中间件并添加额外的约束
您可以扩展中间件并添加一些额外的检查:
class CustomStartSession extends \Illuminate\Session\Middleware\StartSession {
public function storeCurrentUrl(Request $request, $session){
if(!ends_with($request->url(), '.js')){
parent::storeCurrentUrl($request, $session);
}
}
}
然后通过将 app/Http/Kernel.php
中的原始 StartSession
替换为您的来启用它,任何 URL 以 .js
结尾的请求都不会保存为“上一个 url"
更新
显然,中间件需要注册为单例才能发挥全部功能。您可以通过将此添加到您的服务提供商之一的 register
方法中来做到这一点(例如 AppServiceProvider
)
$this->app->singleton('App\Http\Middleware\CustomStartSession');
我知道这很丑陋,但您可以通过在控制器中将请求方法更改为 POST 来避免 storeCurrentUrl()。
$request->setMethod('POST');
当没有设置 HTTP_REFERER
时,redirect()->back()
出现问题。
通常当我打开我的 URL 并使用 ID 作为参数显示特定数据行时:
http://my-domain/module/ID
如果指定的ID
不存在或者当前登录用户没有权限打开,我就
if (!$this->checkPermission($id)) {
return redirect()->back()->withError('message');
}
// do my stuff to show the datarow
但是当我更改浏览器地址字段并加载一个不允许的 ID
时,就没有 HTTP_REFERER
设置并且 redirect()->back()
将我重定向到我上次加载的 JavaScript for_this_view.js
(并显示)在视图中添加如下:
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
redirect()->back()
在这里做什么?
如果没有 URL 重定向回来 laravel 猜一个?为什么?也许永远有办法重定向。
这让我无法使用这个功能。
更新
啊,也许我发现了我的问题...
如果 JavaScript 存在于特定文件夹中,我会尝试动态加载它们。
我这样做是因为我希望我的 JavaScript 像视图一样被解析以在其中获取 Blade 语法。我在 /config/view.php
中添加了一个特定的文件夹,并制作了 for_this_view_js.blade.php
之类的文件。它们在 routes.php
.
for_this_view.js
因此我做了类似的东西:
/* 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;
});
}
}
我目前的解决方法是像这样扩展 BaseController
:
<?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('/');
}
}
}
然后这样称呼它:
<?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';
}
}
如果未设置 HTTP_REFERER
,Laravel 会尝试从会话中获取最后一个请求。此 URL 存储在 Illuminate\Session\Middleware\StartSession
:
protected function storeCurrentUrl(Request $request, $session)
{
if ($request->method() === 'GET' && $request->route() && ! $request->ajax())
{
$session->setPreviousUrl($request->fullUrl());
}
}
如您所见,不幸的是您的 JS 请求满足了一些条件:
- GET 方法
- 由路由处理
- 不是 ajax 请求
解决方案
在我看来,你有两个选择。
禁用中间件
警告:我还没有测试过这个,可能会有副作用
转到 app/Http/Kernel.php
并从 $middleware
中删除 'Illuminate\Session\Middleware\StartSession',
。现在,如果没有设置 HTTP_REFERER
,Laravel 将简单地重定向到您的应用程序的根目录 (/
)
扩展中间件并添加额外的约束
您可以扩展中间件并添加一些额外的检查:
class CustomStartSession extends \Illuminate\Session\Middleware\StartSession {
public function storeCurrentUrl(Request $request, $session){
if(!ends_with($request->url(), '.js')){
parent::storeCurrentUrl($request, $session);
}
}
}
然后通过将 app/Http/Kernel.php
中的原始 StartSession
替换为您的来启用它,任何 URL 以 .js
结尾的请求都不会保存为“上一个 url"
更新
显然,中间件需要注册为单例才能发挥全部功能。您可以通过将此添加到您的服务提供商之一的 register
方法中来做到这一点(例如 AppServiceProvider
)
$this->app->singleton('App\Http\Middleware\CustomStartSession');
我知道这很丑陋,但您可以通过在控制器中将请求方法更改为 POST 来避免 storeCurrentUrl()。
$request->setMethod('POST');