Dynamic middleware for laravel 5

2019-03-19 07:21发布

问题:

While building multi-tenancy packages for Laravel 5 I had to find out how to add middleware dynamically from code. In comparison to this question on SO I do not want to touch the Http/Kernel definitions.

During application initialization I check whether the requested hostname is known in the database and whether this hostname requires a redirect to a primary hostname or ssl.

Because you don't want to touch the Http/Kernel as a package, we need to use the service provider.

Requirements:

  • dynamically add middleware without touching Http/Kernel
  • use service provider and response object instead of "hacks"

回答1:

The solution is to dynamically register the middleware in the kernel. First write your middleware, for instance:

<?php namespace HynMe\MultiTenant\Middleware;

use App;
use Closure;
use Illuminate\Contracts\Routing\Middleware;

class HostnameMiddleware implements Middleware
{
    public function handle($request, Closure $next)
    {
        /* @var \HynMe\MultiTenant\Models\Hostname */
        $hostname = App::make('HynMe\Tenant\Hostname');
        if(!is_null($redirect = $hostname->redirectActionRequired()))
            return $redirect;

        return $next($request);
    }
}

Now in your service provider use the following code in the boot() method to add this middleware to the Kernel:

$this->app->make('Illuminate\Contracts\Http\Kernel')->prependMiddleware('HynMe\MultiTenant\Middleware\HostnameMiddleware');

To answer what redirectActionRequired() method does in the hostname object:

/**
 * Identifies whether a redirect is required for this hostname
 * @return \Illuminate\Http\RedirectResponse|null
 */
public function redirectActionRequired()
{
    // force to new hostname
    if($this->redirect_to)
        return $this->redirectToHostname->redirectActionRequired();
    // @todo also add ssl check once ssl certificates are support
    if($this->prefer_https && !Request::secure())
        return redirect()->secure(Request::path());

    // if default hostname is loaded and this is not the default hostname
    if(Request::getHttpHost() != $this->hostname)
        return redirect()->away("http://{$this->hostname}/" . (Request::path() == '/' ? null : Request::path()));

    return null;
}

If you need to dynamically register routeMiddleware use the following in your service provider;

$this->app['router']->middleware('shortname', Vendor\Some\Class::class);

Please add comments if you have questions about this implementation.