1

I am trying to get api versioning in place for an API I am working on, I found this post that explained how to do it using middleware and replacing a string in the route itself. Basically specifying routes like this.

Route::group(['middleware' => ['api-version']], function() {
  Route::get('/endoint', ['uses' => '{api-namespace}\EndpointController@endpoint']);
});

However, when I attempt this I get the following error

Class App\Http\Controllers\{api-namespace}\EndpointController does not exist

It would appear that the container is verifying the existence of route controller files prior to running the middleware which does the replacing. I have added the middleware to the $routeMiddleware in the Http Kernel file.

How can I accomplish this before it checks for the existence of the file?

I thought about adding this to the applications global middleware but I do not want this to run on web only on api calls

Joseph Crawford
  • 1,470
  • 1
  • 15
  • 29
  • show the controller plz – Mahdi Younesi Feb 09 '18 at 16:53
  • Controller does not matter, the request is not even making it to the middleware so it is as well not making it to the controller. It seems like it is failing at line 749 of Illuminate\Container\Container.php. It also happens with any route that has the {api-namespace} in the controller definition. – Joseph Crawford Feb 09 '18 at 17:39

3 Answers3

2

Create a different file for next version of API has some downside. You have to create all the routes from version 1

and in my case version 2 was just some changes to 3 requests. that was the time I felt we need a fallback for this kind of operation.

then I created a simple Laravel package to support Laravel API versioning it adds fallback functionality to routes. I personally needed this long ago but didn't realize it will be achieved with such a tiny package.

https://github.com/mbpcoder/laravel-api-versioning

Mahdi Bagheri
  • 86
  • 1
  • 8
  • That is a small package and very nice work I must say. This should be how Laravel handles it by default, you might want to think about creating a PR :D – Joseph Crawford Aug 27 '20 at 19:31
0

The problem is that uses actually tries to retrieve a class and then call the method inside, you shouldn't be encouraged to put any parameters there so restrain from doing so, instead try grouping your api routes under certain prefix and middleware like so:

Route::prefix('XXXXXXX')->group(['middleware' => ['api-version']], function() {
    Route::get('/endoint', 'EndpointController@endpoint');
});

Note: My above assumption is made out of that you didn't handle changing {api-namespace} inside of your middleware class properly.

Nikola Gavric
  • 3,507
  • 1
  • 8
  • 16
  • This is not what I am looking for, route prefixes require the api version to be in the URL so it would have to be /api/v1/... I want the api version to be in the headers which is why I am trying to do this through middleware. – Joseph Crawford Feb 09 '18 at 19:15
  • Sure the middleware handle method is here: https://paste.laravel.io/09b0408e-fc19-4a7b-862d-e6aea1019f1a However, it is not making it to that code when I have {api-namespace} in my route which is what I am trying to get around. I would like to replace that before the Container does it's checking for the file. – Joseph Crawford Feb 09 '18 at 20:17
  • Sorry, that was a modified version when I was trying different things. This is the original: https://paste.laravel.io/e8c2dceb-d0de-44d4-82c1-ffa8cb6e21de – Joseph Crawford Feb 09 '18 at 20:22
  • Try removing `['uses' =>`, so you end up with `Route::get('/endoint', '{api-namespace}/EndpointController@endpoint');`. Do you still get the same error? – Nikola Gavric Feb 09 '18 at 20:25
  • Try with `Route::middleware('api-version')->group(function() { Route::get('/endoint', '{api-namespace}/EndpointController@endpoint'); })` – Nikola Gavric Feb 09 '18 at 20:34
  • missing second argument for middleware. looking to find what it needs – Joseph Crawford Feb 09 '18 at 20:50
  • Try putting the middleware name in an array, like this `Route::middleware(['api-version'])` – Nikola Gavric Feb 09 '18 at 20:54
  • still complains about the second argument – Joseph Crawford Feb 09 '18 at 21:01
  • The only thing that comes to my mind more is to actually remove `[]` from middleware name and just have a string: `Route::group(['middleware' => 'api-version']...` – Nikola Gavric Feb 09 '18 at 21:04
  • That would limit my routes to a single middlware item though and I need to run several middleware the above code is pseudo code but the same exact structure as what I have – Joseph Crawford Feb 09 '18 at 21:11
0

Stepping through the code allowed me to see that this is already handled by Laravel and all i needed to do was create a routes/api/v2.php file with the routes for version 2. The only problem I see is having to duplicate all routes which did not change from version 1 to version 2. I may look into modifying my RouteServiceProvider to actually inherit previous versions if they are not overridden in the requested api version rather than duplicating the routes code for every api version.

Joseph Crawford
  • 1,470
  • 1
  • 15
  • 29