可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am building APIs for my Android app using laravel and default session driver set to REDIS.
I found a good article here http://dor.ky/laravel-prevent-sessions-for-routes-via-a-filter/ which sort of serves the purpose.
However when ever I hit the url it also hits the redis and generates the key which is empty. Now I want avoid creating empty session keys in redis. Ideally it should not hit the redis How can I do that?
Can we customise sessios in a way so that sessions are generated only for specific routes (or disable for specific routes)?
I can explain more with specific use case, please let me know.
回答1:
Its really easy using the middleware in Laravel 5, I needed any request with an API key not to have a session and I simply did :
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Session\Middleware\StartSession as BaseStartSession;
class StartSession extends BaseStartSession
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(\Request::has('api_key'))
{
\Config::set('session.driver', 'array');
}
return parent::handle($request, $next);
}
}
Also you will need to extend the SessionServiceProvider as follows:
<?php namespace App\Providers;
use Illuminate\Session\SessionServiceProvider as BaseSessionServiceProvider;
class SessionServiceProvider extends BaseSessionServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerSessionManager();
$this->registerSessionDriver();
$this->app->singleton('App\Http\Middleware\StartSession');
}
}
and place in your config/app.php
under providers
:
'App\Providers\SessionServiceProvider',
Also you must change it in your kernel file: App/Http/Kernel.php
, in the $middlewareGroups
section change the default entry, \Illuminate\Session\Middleware\StartSession::class,
to your new class \App\Http\Middleware\StartSession::class,
.
回答2:
In Laravel 5, just don't use the StartSession
, ShareErrorsFromSession
, and VerifyCsrfToken
middlewares.
In my application I've moved these three middlewares from the web
group to a new stateful
group, and then I have included this stateful
group on routes which need to know about the session (in addition to web
in all cases, in my app at least). The other routes belong to either the web
or api
groups.
Now when making requests to the routes which are not using the stateful
middleware group session cookies are not sent back.
回答3:
The simplest way to achieve this is to Make your own AppStartSession middleware that subclasses Illuminate\Session\Middleware\StartSession and the replace the class being used in kernel.php. The only method you need to override in your subclass is sessionConfigured() for which you can return false to disable the session or parent::sessionConfigured() to allow it.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Session\Middleware\StartSession;
class AppStartSession extends StartSession
{
protected function sessionConfigured(){
if(!\Request::has('api_key')){
return false;
}else{
return parent::sessionConfigured();
}
}
}
kernel.php (see *** comment for where the change is done)
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* @var array
*/
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
// *** Replace start session class
// \Illuminate\Session\Middleware\StartSession::class,
\App\Http\Middleware\AppStartSession::class,
// *** Also comment these ones that depend on there always being a session.
//\Illuminate\View\Middleware\ShareErrorsFromSession::class,
//\App\Http\Middleware\VerifyCsrfToken::class,
];
/**
* The application's route middleware.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
];
}
Don't fight the framework, embrace it!
回答4:
Since Laravel 5.2, when middleware groups were introduced, you may disable session for certain routes by defining them outside of the "web" middleware group (which includes the StartSession middleware responsible for session handling). As on latest 5.2.x versions the whole default routes.php file is wrapped with "web" middleware group, you need to make some modification in app/Providers/RouteServiceProvider.php
file, as described here.
回答5:
I've been trying to accomplish a similar feature.
Our API is stateless except for 1 route - the version 1 cart.
I ended up with setting 'driver'
in the app/config/session.php like this ...
'driver' => 'v1/cart' === Request::getDecodedPath() ? 'native' : 'array',
Nothing magic. Initially we though of using a before filter, but that wasn't happening early enough.
It seems a simple way to do things, but I may be missing something.
Putting the switch in the config seems an easy place for other developers to see what the driver is whereas putting it in a service provider is so tucked out of the way, without knowing what service providers are installed and what they interact with, it would be far harder to debug.
Anyway. Hope this is of some use.
As pointed out below ... DO NOT CACHE YOUR CONFIG IF IT IS DYNAMIC.
Which does lead to it being of limited use. As soon as we no longer need to support v1/cart, we will be dropping this route and then be back on a static config.
回答6:
There appears to be a way to accomplish this using a session reject callback.
Relevant sources...
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Foundation/Application.php#L655
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Foundation/Application.php#L660
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Session/Middleware.php#L60
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Session/Middleware.php#L97
I can't find many references to this around the web, but reading more through the source it appears that if the session reject callback returns a truthy value, the session will be forced to use an array driver for the request rather than whatever is configured. Your callback also gets the current request injected so you can do some logic based on the request parameters.
I've only tested this on a local Laravel 4.2 install but it seems to work. You just need to bind a function to session.reject.
First, create a SessionRejectServiceProvider (or something like that)
<?php
use \Illuminate\Support\ServiceProvider;
class SessionRejectServiceProvider extends ServiceProvider {
public function register()
{
$me = $this;
$this->app->bind('session.reject', function($app)use($me){
return function($request)use($me){
return call_user_func_array(array($me, 'reject'), array($request));
};
});
}
// Put the guts of whatever you want to do in here, in this case I've
// disabled sessions for every request that is an Ajax request, you
// could do something else like check the path against a list and
// selectively return true if there's a match.
protected function reject($request)
{
return $request->ajax();
}
}
Then add it to your providers in your app/config/app.php
<?php
return array(
// ... other stuff
'providers' => array(
// ... existing stuff...
'SessionRejectServiceProvider',
),
);
Edit / More Info
The net result is that the reject() method is called on every request to your application, before the session is started. If your reject() method returns true, sessions will be set to the array driver and basically do nothing. You can find a lot of useful info the $request parameter to determine this, here's the API reference for the request object in 4.2.
http://laravel.com/api/4.2/Illuminate/Http/Request.html