I've just test the API using Postman. When I don't using sub-directory, the API works fine like this
But if I using sub-directory, it returns 404 Not Found, like this, even I configure nano/etc/nginx/sites-available/mobilku
by adding this line following this link :
the question is, how to make API routes from subfolder still works like picture in above? Here is my example code for get car using API
Route::prefix('car')->group(function () {
Route::get('/', [CarController::class, 'all'])->name('car');
Route::get('/details/{id}', [CarController::class, 'details'])->name('car.detail');
Route::post('/search', [CarController::class, 'search'])->name('car.search');
Route::post('/filter', [CarController::class, 'filter'])->name('car.filter');
});
I just read this link Laravel routes not working with subfolder, but I'm not sure that answer still works for Laravel 8 and 9.
CodePudding user response:
laravel web app configuration
According to the laravel v8 and v9 documentation, there are no big changes from the laravel v5 (besides that your routes should now start with the /
prefix unlike it was in v5). Both answers from the referred thread also suggests to use a RouteServiceProvider
(v8, v9) to define such a prefix globally.
However when you place your app under an additional URI prefix, it will see all the routes as /mobilku/api/...
rather than /api/...
, so you should change some other code part of your laravel app that you didn't show in your question to made it ready for such a change (or you can somewhat "spoof" the URI it will see from it's side; see the last part of the answer). You may also consider to just rename an /api
URI prefix to the /mobilku
one.
nginx configuration
Serving a PHP web app under an URI prefix using the alias
directive is a bit tricky due the the long standing side effects of the try_files
directive used together with the alias
one, which are unlikely to be fixed at least until the major nginx version gets changed since there are too many configurations exists adopted to those effects in some way. Commonly used workarounds are:
duplicating the
try_files
directive last argument prefix:location /mobilku { alias var/www/html/mobilku/public; try_files $uri $uri/ /mobilku/mobilku/index.php$args; location ~ \.php$ { include snippet/fastcgi-php.conf; fastcgi_param SCRIPT_FILENAME $request_filename; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } }
using an
if
instead of thetry_files
:location /mobilku { alias var/www/html/mobilku/public; if (!-e $request_filename) { rewrite ^ /mobilku/index.php last; } location ~ \.php$ { include snippet/fastcgi-php.conf; fastcgi_param SCRIPT_FILENAME $request_filename; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } }
using an additional named location as the
try_files
directive last argument:location /mobilku { alias var/www/html/mobilku/public; try_files $uri $uri/ @mobilku; location ~ \.php$ { include snippet/fastcgi-php.conf; fastcgi_param SCRIPT_FILENAME $request_filename; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } } location @mobilku { rewrite ^ /mobilku/index.php last; }
However none of those workarounds are needed when you are serving an API web app that doen't have any static files at all, and any incoming request should be served by the index.php
controller. The much more efficient way, without traversing the request though different location, checking the static files existence and matching an URI against the \.php$
regex pattern (invoking an expensive PCRE library call) will be the following:
location /mobilku {
root /var/www/html/mobilku/public;
rewrite ^ /index.php break;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$uri;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
Spoofing a request URI that will be seen by the laravel app
An original request URI seen by laravel application is passed to the PHP-FPM backend via the REQUEST_URI
FastCGI variable, which is being defined inside the fastcgi_params
file as
fastcgi_param REQUEST_URI $request_uri;
If for some reason you can't change your backend laravel (or any other PHP based) web app, you still can change the REQUEST_URI
FastCGI variable value that is being passed to the backend.
That $request_uri
is an internal read-only nginx variable, so you can't change its value using set
directive. However you can redefine its value after it will be defined inside the fastcgi_params
file. The PHP-FPM FastCGI backend will use the last defined value (although other FastCGI backends can behave differently; you can check this ServerFault thread to find out more some details). To strip the /mobilku
URI prefix from the REQUEST_URI
FastCGI variabe you can use an additional rewrite
directive:
location /mobilku {
root /var/www/html/mobilku/public;
rewrite /mobilku/?(.*) /$1;
set $tweaked_uri $uri$is_args$args;
rewrite ^ /index.php break;
include fastcgi_params;
fastcgi_param REQUEST_URI $tweaked_uri;
fastcgi_param SCRIPT_FILENAME $document_root$uri;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
or the following one to replace the /mobilku
prefix with the /api
:
location /mobilku {
root /var/www/html/mobilku/public;
rewrite /mobilku/?(.*) /api/$1;
set $tweaked_uri $uri$is_args$args;
rewrite ^ /index.php break;
include fastcgi_params;
fastcgi_param REQUEST_URI $tweaked_uri;
fastcgi_param SCRIPT_FILENAME $document_root$uri;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
However it can be unreliable if your original URI contain some characters that are getting url-decoded during the URI normalization process (for example, some special characters or two bytes UTF-8 characters from national alphabets) because the rewrite
directive (as well as the location
one) works with the normalized URI while a PHP backend expect to see an url-encoded one. A more reliable way would be to use a map
block to strip or replace URI prefix, e.g.
map $request_uri $tweaked_uri {
~^/mobilku/?(.*) /$1;
default $request_uri;
}
or
map $request_uri $tweaked_uri {
~^/mobilku/?(.*) /api/$1;
default $request_uri;
}
Then you can use a mapped $tweaked_uri
variable the same way as shown in the previous example:
location /mobilku {
root /var/www/html/mobilku/public;
rewrite ^ /index.php break;
include fastcgi_params;
fastcgi_param REQUEST_URI $tweaked_uri;
fastcgi_param SCRIPT_FILENAME $document_root$uri;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
CodePudding user response:
The Laravel application understands that it is at the root of the server by default.
Try this, I hope it helps you:
Add APP_DIR environment variable to your .env file, containing the subdirectory where Laravel is installed into, like showed below:
APP_DIR = "laravel_subdir"
Then create a Helper Provider to define our custom functions, create a file HelperServiceProvider.php inside Providers directory, with the following code:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider{
/**
* Bootstrap services.
*
* @return void
*/
public function boot(){
}
/**
* Register services.
*
* @return void
*/
public function register(){
foreach (glob(app_path() . '/Helpers/*.php') as $file) {
require_once($file);
}
}
}
Then, at the level of App directory, add Helpers directory, and inside it, create a file SubdirectoryAssetsHelper.php, with the following code:
<?php
if (! function_exists('subdirAsset')) {
function subdirAsset($path){
return asset( (App::environment('production') ? env('APP_DIR') : '')."/".$path);
}
}
if (! function_exists('subdirMix')) {
function subdirMix($path){
return mix( (App::environment('production') ? env('APP_DIR') : '')."/".$path);
}
}
Now register the provider by adding this line to config/app.php file:
App\Providers\HelperServiceProvider::class
Now replace the function mapWebRoutes of file RouteServiceProvider.php, like showed below:
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes(){
Route::prefix(App::environment('production') ? env('APP_DIR') : '')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
And import App class in top of file:
use App; That’s all, now you can use function subdirAsset instead of asset and subdirMix instead of mix inside your blade files.
Switch environment If you are using valet or artisan serve, keep your APP_ENV variable to “local”:
APP_ENV = local
If you are in a production environment of shared hosting, use:
APP_ENV = production