Web middleware does not get executed (Laravel 5.5) - laravel

I'm trying to create a website with multiple languages (with Laravel 5.5). Therefore I follow the tutorial https://mydnic.be/post/how-to-build-an-efficient-and-seo-friendly-multilingual-architecture-for-your-laravel-application, but somehow I have troubles with the language middleware part:
// BeforeLanguage.php
namespace App\Http\Middleware;
use Closure;
class BeforeLanguage
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// Check if the first segment matches a language code
if (!array_key_exists($request->segment(1), config('translatable.locales')) ) {
// Store segments in array
$segments = $request->segments();
// Set the default language code as the first segment
$segments = array_prepend($segments, config('app.fallback_locale'));
// Redirect to the correct url
return redirect()->to(implode('/', $segments));
}
return $next($request);
}
}
It works if I open a URL with the identifier for the language, e. g. http://ps.dev/de/app/login but I get "Sorry, the page you are looking for could not be found." if I try to open the page without the language identifier in the URI, e. g. http://ps.dev/app/login.
But here I would expect that the middleware adds the language segment to the URI. Any idea what could go wrong?
Below I would like to provide some additional information.
web.php
Route::prefix('app')->group(function () {
// Authentication routes
Route::get('login', 'SessionsController#create')->name('login');
// ...
});
Kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\BeforeLanguage::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
According to the Laravel documentation page the middlewares assigned to the middleware group web get executed by default for any route defined inside web.php. (Out of the box, the web middleware group is automatically applied to your routes/web.php file by the RouteServiceProvider.)
RouteServiceProvider.php
protected function mapWebRoutes()
{
$locale = Request::segment(1);
Route::prefix($locale)
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}

I think you had chosen a hard way something like "Reinvent the Wheel!" ;)
Although It's your decision and respectful, as a suggestion try this nice package for localization in Laravel :D

The reason why it is sending to the 404 is that the url doesn't exist. In your route file, you have not created the paths for language. So ultimately, if your
middleware works it will send to the non-existing route path, which will be 404.
Add an identifier for the language :-
Route::get('{lang}/login', 'SessionsController#create')->name('login');
Check you routes by the following artisan command in cmd
php artisan route:list

Related

Where to switch to other service depending on logged user field?

In laravel 9 app I try to switch to different service
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
if ($this->app->environment('local')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(TelescopeServiceProvider::class);
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
// Error pointing at this line
$loggedUser = Auth::user();
if( $loggedUser->membership_mark === 'G') { // G=>Gold Membership
$this->app->bind('App\Interfaces\PageMembershipMarkStrategyInterface',
'App\Implementations\PageGoldMemberStrategy');
}
}
But I got error :
Target class [hash] does not exist. in...
Looks like register method is not proper place for app->bind when I need to get logged user.
But where have I to make this binding? In custom service provider? Im middleware ?
MODIFIED BLOCK :
I create a middleware with command :
php artisan make:middleware SetPageMembershipMarkService
and in app/Http/Kernel.php I added it :
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
// I added this line :
\App\Http\Middleware\SetPageMembershipMarkService::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
But I got error :
Illuminate\Contracts\Container\BindingResolutionException: Target [App\Interfaces\PageMembershipMarkStrategyInterface] is not instantiable while building [App\Http\Controllers\InformOnPageInStockController]....
I am not sure in which place of Kernel.php I have to add ref to PageMembershipMarkStrategyInterface ?
checking log it appears that my SetPageMembershipMarkService is not triggered before error...
MODIFIED BLOCK 2:
In file app/Http/Kernel.php I added my Middleware reference:
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
// I set it AFTER auth
'setPageMembership' =>\App\Http\Middleware\SetPageMembershipMarkService::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminat`enter code here`e\Auth\Middleware\EnsureEmailIsVerified::class,
];
abd in routes/api.php :
Route::middleware('auth:api')->prefix('v1') /*->name('api.v1')*/ ->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
...
Route::middleware(['setPageMembership'])->group(function () {
Route::resource('/inform-on-page-in-stock', InformOnPageInStockController::class)->except(['edit', 'add']);
});
and I still got error :
Illuminate\Contracts\Container\BindingResolutionException: Target [App\Interfaces\InformOnPageInStockStrategyInterface] is not instantiable while building [App\Http\Controllers\InformOnPageInStockController].
Checking logs I do not find log message I set in SetPageMembershipMarkService middleware, so looks like it does
not set before my controllers...
What is wrong ?
You mentioned using of RouteServiceProvider. Which is correct syntax ?
File app/Http/Middleware/SetPageMembershipMarkService.php has lines :
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Auth;
class SetPageMembershipMarkService
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$loggedUser = Auth::user();
\Log::info(varDump($loggedUser, ' -1 SetPageMembershipMarkService $loggedUser::'));
$membershipMark = $loggedUser->membership_mark;
if( $membershipMark === 'S') { // G=>Silver Membership
app()->bind('App\Interfaces\InformOnPageInStockStrategyInterface',
'App\Implementations\HasNoInformOnPageInStockStrategy');
}
if( $membershipMark === 'G') { // G=>Gold Membership
app()->bind('App\Interfaces\InformOnPageInStockStrategyInterface',
'App\Implementations\InformOnPageInStockGoldMembersStrategy');
}
return $next($request);
}
}
But in log file I do not see any log message I have in this Middleware...
Thanks!
You will not be able to get Auth:user() data in Service Provder. Because service provider runs before Laravel sets the Auth data, you'll have to use some hacky methods to handle it.
I think better ways to handle data related to auth would be on middleware and not in Service Provider.
Additional explanations can be found on this questions answer Laravel - How to get current user in AppServiceProvider
EDIT for modifed block
Your middleware registration is wrong.
$routeMiddleware this array is used to register a custom middleware which you gonna use and define manually
For example:
protected $routeMiddleware = [
.....
// I added this line :
'setPageMembership' => \App\Http\Middleware\SetPageMembershipMarkService::class,
Then you gonna use this middleware in your route groups like:
Route::middleware(['auth', 'setPageMembership'])->group(function () {
Route::get('test', [TestController::class, 'test']);
});
If you want the middleware to run for all the routes it is better to add it in the $middleware array or if you want in the certain route group (web/api) then in the $middlewareGroups individual web and api array.
Adding the middleware in RouteServiceProvder also works if you want every route to have the middleware.
However, for your use case, authentication is required before binding. So, you will need auth middleware to fire first before you perform those bind actions. So adding the middleware to your route file like I showed above will be enough for your use case.

allow only requests from domain and block other sources

I want to allow some routes to only respond to requests made by my front-end website, meaning block other sources like postman and allow only the request from domain of the front-end for security reasons.
Is it possible?
for example, I have a webpage to check the dynamic value of the link and verify if the token on link is on database or not, I can think of putting captcha so a bot can't check all possible combinations, but it's not 100% safe.
Laravel API Throttling
if your main problem is bots getting all combinations, the throttling middleware alongside using captchas will help you with that.
By default, all your API routes (in routes/api.php) allow for a maximum of 60 requests per minute per IP. You can modify this amount to your own need in app/Http/Kernel.php file by changing the throttle:api section:
/**
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
changing it to throttle:30:1 for example will mean you will allow 30 requests per minute per ip.
if you only want some routes on your api to be throttled, you can use the middleware elsewhere:
Route::get('my-method', MyController::class)->middleware('throttle:30:1');
Create a middleware that limits hosts
if you want to limit exactly by domain, what you are looking for is probably a custom middleware. Middlewares allow you to inspect various request properties (including the request's host through $request->getHost()) and prevent any controllers or methods.
Although Laravel's default TrustHosts middleware provides a global host validation, you could create your own custom middleware for specific paths that would look like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class LocalOnly
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if($request->getHost() != 'localhost')
{
return response('', 400);
}
return $next($request);
}
}
Note: if you are creating new middlewares, you will need to register them. Laravel has its own guide on this here.
in this example, when used on any route, Laravel will reject any host other than localhost (so even 127.0.0.1 will be rejected)
Personally, I don't recommend doing this as the built-in throttling is a much more elegant solution but in case you really need to do it, here you go.
You can simply do this by creating middleware. I added some more efficient way to add restrications.
1- Create Middleware ApiBrowserRestricationMiddleware
2- Add this \App\Http\Middleware\ApiBrowserRestricationMiddleware::class in
App\Http\Kernel $middleware array
3- Add the code in ApiBrowserRestricationMiddleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ApiBrowserRestricationMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if(in_array($request->getHost(), ['127.0.0.1']) == false)
{
return response('', 400);
}
return $next($request);
}
}

Laravel - limiting users for number of posts

Is there any way to limit users in my project for number of posts. For example, I want my users can create a maximum 10 posts each one. So one user has 10 posts? Is it something with hasMany or something else? Please help find a solution. Thank you
By definition
Middleware provide a convenient mechanism for filtering HTTP requests
entering your application. For example, Laravel includes a middleware
that verifies the user of your application is authenticated. If the
user is not authenticated, the middleware will redirect the user to
the login screen. However, if the user is authenticated, the
middleware will allow the request to proceed further into the
application.
To prevent user from adding more than 10 posts you need to create a middleware to protect your posts/create route
To create a new middleware, use the make:middleware Artisan command:
php artisan make:middleware CheckUserPostsNumber
This command will place a new CheckUserPostsNumber class within your app/Http/Middleware directory. In this middleware, we will only allow access to the posts/create route if the user posts number < than 10. Otherwise, you will redirect the user back to the home URI:
<?php
namespace App\Http\Middleware;
use Illuminate\Support\Facades\Auth;
use Closure;
class CheckUserPostsNumber
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::user()->posts->count() >= 10) {
return redirect('home');
}
return $next($request);
}
}
Assigning Middleware To Routes
you would like to assign middleware to specific routes, you should first assign the middleware a key in your app/Http/Kernel.php file. By default, the $routeMiddleware property of this class contains entries for the middleware included with Laravel. To add your own, append it to this list and assign it a key of your choosing:
// Within App\Http\Kernel Class...
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
//...
'checkUserPostsNumber' => 'App\Http\Middleware\checkUserPostsNumber'
];
Once the middleware has been defined in the HTTP kernel, you may use the middleware method to assign middleware to a route:
Route::get('posts/create', function () {
//
})->middleware('auth', 'checkUserPostsNumber');
Docs
if ($user->posts->count() >= 10) {
//
}

Looking for some helps in adding policy to my laravel projects

I am new in learning laravel. Currently I have been stuck in the Laravel Policy,could you please kindly help with directing me how to add policy to my project?
I would like to make just only the Administrator User be able to see the 'Administration Dashboard' by using Laravel Policies Rules but failed. Actually every registered user is able to see that entrance(just like the attached picture showing below).
The user name is uu#gmail.com and the password is uuuuuu.
Please go to my testing website http://lookoko.com/ and log with the user name and password you will see that Administration Dashboard list in the drop-down lists.
To create a new middleware, use the make:middleware Artisan command:
php artisan make:middleware AdminMiddleware
This command will place a new AdminMiddleware class within your app/Http/Middleware directory. In this middleware, we will only allow access to the route if the logged-in user is admin. Otherwise, we will redirect the users back to the home URI.
<?php
namespace App\Http\Middleware;
use Closure;
class AdminMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (! auth()->user()->is_admin) {
return redirect()->route('home');
}
return $next($request);
}
}
Here I am assumming you have a column in user table named is_admin
Registering Middleware
You should assign the middleware a key in your app/Http/Kernel.php file. By default, the $routeMiddleware property of this class contains entries for the middleware included with Laravel. To add your own, simply append it to this list and assign it a key of your choosing. For example:
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
...
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'admin' => \App\Http\Middleware\AdminMiddleware::class,
];
Once the middleware has been defined in the HTTP kernel, you may use the group method to assign middleware to group of routes:
Route::group(['middleware' => ['admin']], function () {
// all your admin routes comes here
});
Docs

Lavavel 5: Error page middlewares are not accessible

I'm using Laravel 5.0 and getting one issue. My site is working fine when there is no error like 404, 500 etc. But when there is error on my site then it render error page but some middlewares are not loading.
My site has two parts frontend and backend. Both parts have diff-diff middlewares which I'm using. I'm using some number of custom middlewares, some of them I'm using on frontend and some of them using on backend. But when there is any error on site then middlewares are not working which are loaded in $routeMiddleware array in Kernel.php file.
I research number of articles but all gives me same solution to load all required middlewares in $middleware, which seems not fine to me. Because in $middleware array all middlewares are loading for both fronend and backend sections. But I need to load spacial middlewares only single section. How could I implement this.
This is my kernel.php file
class Kernel extends HttpKernel {
/**
* The application's global HTTP middleware stack.
*
* #var array
*/
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
// 'App\Http\Middleware\VerifyCsrfToken',
];
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.frontend' => 'App\Http\Middleware\FrontendAuthenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
];
}
And this is my route.php file code
Route::group(['middleware' => 'auth.frontend'], function() {
Route::get('/home', ['as' => 'home', 'uses' => 'HomeController#index']);
Route::get('/inner_page', ['as' => 'inner_page', 'uses' => 'HomeController#inner_page']);
});
How this is possible, please help me on this issue.
As explained in documentation, you must define a terminate($request, $response) method to run the middle-ware after the response is ready to be sent to the browser. The terminate($request, $response) method should receive both the request and the response.
then you can either add your middle-ware to the global or route middle-ware arrays.
example :
<?php
namespace App\Http\Middleware;
use Closure;
class FrontendAuthenticate
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
// Check if the $response is an error and then do stuff
}
}

Resources