jacksierkstra's avatar

Target specific controllers based on headers in middleware

I have made a simple open source project to switch different API versions based on header values. The Github repo can be found here.

I have defined middleware so I can switch to different implementations of controllers. The middleware implementation can be found here.

How to do the following: My code detects whether a given header value is present. So:

  • If this header value is present: api_version: v1
    • I want to target my controller : App\Http\Controllers\Api\v1\ApiController
  • If this header value is present: api_version: v2
    • I want to target : App\Http\Controllers\Api\v2\ApiController

How to setup my routes.php so it can only be accessible by supplying a header value?

And how do I go to that control from within my middleware?

0 likes
5 replies
JarekTkaczyk's avatar

@jacksierkstra

edit: just noticed you asked about lumen :) I guess in this case you need to do that directly in routes.php.

Middleware is fired too late, so here's what you can do:

// app/Providers/RouteServiceProviders.php

    public function map(Router $router)
    {
        $version = $this->app['request']->header('version');

        switch ($version) {
            case 'v1':
                $namespace = 'Your\V1\Namespace';
                break;
            case 'v2':
                $namespace = 'Your\V2\Namespace';
                break;
            default:
                $namespace = 'Your\V3\Namespace';
        }

        $router->group(['namespace' => $namespace], function ($router) {
            require app_path('Http/routes.php');
        });
    }

I suppose you could hack it from middleware somehow, but I don't think it's worth it.

jacksierkstra's avatar

Thanks Jarek for the point of direction. Why do you consider the middleware as a hack in this scenario? Is this the order in which laravel processes the requests?

  • Routes
  • Middleware
  • Controller

Or are there more layers? And if so, please correct me. Also if this isn't correct. My gut feeling says that middleware is fired before the routes thingy, but that feeling isn't based on anything.

JarekTkaczyk's avatar

@jacksierkstra middleware is part of the HTTP layer and is fired before hitting any route, but it is called after the routes were registered. That's why it would be hacking.

jacksierkstra's avatar

So I made a file RouteServiceProvider.php in the folder app\Providers, with the following content:

<?php

namespace App\Providers;

use App\Parse\Api\RequestParser;
use App\Parse\Api\Version;

use Illuminate\Support\ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{

  public function register() {}

  public function map(Router $router)
  {

    $apiRequestParser = new RequestParser($this->app['request']);

    switch ($apiRequestParser->getApiVersion()) {
        case Version::Version1:
            $namespace = 'App\Http\Controllers\Api\v1';
            break;
        case Version::Version2:
            $namespace = 'App\Http\Controllers\Api\v2';
            break;
        default:
            $namespace = 'App\Http\Controllers\Api\v1';
    }

    $router->group(['namespace' => $namespace], function ($router) {
        require app_path('Http/routes.php');
    });
  }
}

At first it complained I needed a register method, so I added that in. But now, I'm wondering what should I do to register the map method?

Please or to participate in this conversation.