Laravel get middleware for current route - laravel

How do you retrieve the middlewares for the current route?
I'm trying to set the exception handler to work differently based on if you're within a particular part of the website by checking if a middleware has been added to the route.
<?php
namespace App\Exceptions;
//use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Abrigham\LaravelEmailExceptions\Exceptions\EmailHandler as ExceptionHandler;
use Illuminate\Routing\Exceptions\InvalidSignatureException;
use Illuminate\Support\Facades\Route;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* #param \Throwable $exception
* #return void
*
* #throws \Exception
*/
public function report(Throwable $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Throwable $exception
* #return \Symfony\Component\HttpFoundation\Response
*
* #throws \Throwable
*/
public function render($request, Throwable $exception)
{
switch(true) {
case $exception instanceof \Illuminate\Session\TokenMismatchException:
// Redirect back if form invalid
return redirect()
->back()
->withInput($request->except($this->dontFlash))
->withErrors('The form has expired due to inactivity. Please try again');
break;
case $exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException:
// Redirect back with message if model not found error
$redirect = app('redirect');
// Check for different url to prevent redirect loop
if (request()->fullUrl() === $redirect->back()->getTargetUrl()){
// $currentRouteMiddleware = request()->route()->controllerMiddleware() returns empty array
// $currentRouteMiddleware = Route::current()->router->getMiddleware(); router is private
$response = $redirect->to(isset($currentRouteMiddleware['admin.user']) ? '/admin' : '/');
} else {
$response = $redirect->back();
}
return $response
->withInput($request->except($this->dontFlash))
->withErrors('That page could not be found. Please try again or report the broken link: ' . $request->getRequestUri());
break;
}
return parent::render($request, $exception);
}
}
If I dump the current route inside the router it shows the middleware array that I need to check against: dd(Route::current()) but there doesn't appear to be a way of accessing the current router e.g: $currentRouteMiddleware = Route::current()->router->getMiddleware();

There are a couple options based on what you're looking for.
If you want all the middleware aliases assigned to the route, you can use:
Route::current()->gatherMiddleware();
This does not expand assigned middleware groups, so the result from this might look something like:
[
'web',
'admin.user'
]
If you want all of the middleware classes assigned to the route, you can use:
Route::gatherRouteMiddleware(Route::current());
This will give you the classes, but it will not have the associated aliases or groups for the classes. The result from this might look like:
[
"App\Http\Middleware\EncryptCookies",
"Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse",
"Illuminate\Session\Middleware\StartSession",
"Illuminate\View\Middleware\ShareErrorsFromSession",
"App\Http\Middleware\VerifyCsrfToken",
"Illuminate\Routing\Middleware\SubstituteBindings",
"TCG\Voyager\Http\Middleware\VoyagerAdminMiddleware"
]

The Route::getMiddleware() in the Handler is simply a list of \App\Http\Kernel::$routeMiddleware.
dd(Route::getMiddleware());
array:7 [▼
"auth" => "App\Http\Middleware\Authenticate"
"auth.basic" => "Illuminate\Auth\Middleware\AuthenticateWithBasicAuth"
"guest" => "App\Http\Middleware\RedirectIfAuthenticated"
"admin" => "App\Http\Middleware\AdminMiddleware"
// ...
]
It is recommended to declare a constant (or global variable) and check whether the corresponding constant is declared in the Handler to check whether AdminMiddleware has passed.
// app/Http/Middleware/AdminMiddleware.php
public function handle($request, Closure $next)
{
// if ... return ...
define('__APP_IS_ADMIN', true);
return $next($request);
}
// app/Exceptions/Handler.php
public function render($request, Exception $e)
{
// ...
$response = $redirect->to(defined('__APP_IS_ADMIN') ? '/admin' : '/');

request()->route()->controllerMiddleware();

List middlewares applied in the current route inside App\Exceptions\Handler. Using in unauthenticated method.
/**
* Convert an authentication exception into a response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Auth\AuthenticationException $exception
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest(in_array('admin', $request->route()->middleware(), true) ? route('admin.login') : route('login'));
}
I used this to get the "admin" middleware and redirect the user to the correct login page.

Related

Need to Handle Laravel Exception and, Logging details mismatch error

I have tried to handle the exceptions on Handler class in my laravel project
if(!env('APP_DEBUG', false)){
return view('errors.500');
} else {
return parent::render($request, $exception);
}
Errors are redirecting to my page. but in the login page user name or password mismatch also redirecting to that page.
in the login error need to redirect to the login page,not to the common error page. how can handle it?
i have using default laravel auth login.
this is my Handler.php file,
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* #param \Exception $exception
* #return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
// Render well-known exceptions here
// Otherwise display internal error message
if(!env('APP_DEBUG', false)){
return view('errors.500');
} else {
return parent::render($request, $exception);
}
}
}
no need to check for 500 errors manually .If you want to show custom error message for 500 erorrs in production then publish default laravel views for errors
php artisan vendor:publish --tag=laravel-errors
This will generate views for errors in following path
resources/views/errors
To customize 500 erorrs .Edit following path
resources/views/errors/500.blade.php
Also make sure this will only show when
APP_DEBUG=false

Laravel not throwing an error but redirecting to login

In my Laravel project I'm encountering the following behavior which I can't isolate and what is pretty annoying e.g. when I'm sending a request to a controller and either the route or the controller does not exist, Laravel is neither logging the error nor showing the error and but always redirecting to the login page - I've searched around a lot and i may misconfigured something in the project, but can't find out what's the issue.
My Laravel Version: 7.3.4
System: Windows
Server: Wamp with Apache 2.4.39, Mysql 5.7.26, Php Version: 7.3.5
route/web
// Route url
Route::get('/', 'DashboardController#dashboard');
//.. custom routes
Auth::routes();
The Controller looks like this
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DashboardController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
// Dashboard - Ruwido
public function dashboard(){
$pageConfigs = [
'pageHeader' => false
];
return view('/pages/dashboard', [
'pageConfigs' => $pageConfigs
]);
}
}
The Error Handler
<?php
namespace App\Exceptions;
use Throwable;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* #param \Throwable $exception
* #return void
*/
public function report(Throwable $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Throwable $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Throwable $exception)
{
return parent::render($request, $exception);
}
}
Has anybody ever had such a problem?
You are using the middleware auth in the constructor which will redirect to login if the user is not authenticated and will not let the request proceed in the dashboard method. If the user is authenticated that check your default guard, maybe its pointing to api authentication and now that you are using api auth it will not authenticate via session but using api tokens; first try commenting out the middleware in the constructor;

How to return unauthorized using Laravel API Authentication

I am using Laravel API authentication with a token. (as explained here:
https://laravel.com/docs/5.8/api-authentication#protecting-routes)
I am running some tests with Postman and it works fine. When I try to access the route without a valid token, I see that the response is the (html of the) login page of my app. How can I return a Unauthorized message instead of the complete login page? Do I have to create a custom middleware?
Controller
class ExampleController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
public function show(Request $request) {
return response()->json($request->user()->name);
}
}
Please add the method in the class Handler in the file location app/Exceptions/Handler.php
/**
* Convert an authentication exception into an unauthenticated response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Auth\AuthenticationException $exception
* #return \Illuminate\Http\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
return redirect()->guest(route('login'));
}
And also add the following line above the class in the same file as mentioned above:
use Illuminate\Auth\AuthenticationException;
In the postman within the headers section please add the following header :
X-Requested-With:XMLHttpRequest
Hope this helps and resolves the issue. Thanks.
I am using Laravel 8 with passport.
In my case I had to add an "unauthenticated" function inside app/Http/Middleware/Authenticate like so:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
// Add new method
protected function unauthenticated($request, array $guards)
{
abort(response()->json(
[
'api_status' => '401',
'message' => 'UnAuthenticated',
], 401));
}
}
Be sure to be sending the right headers in your request
Content-Type: application/json
Laravel 8 update:
default handler already handle this scenario
File: \Illuminate\Foundation\Exceptions\Handler.php
/**
* Convert an authentication exception into a response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Auth\AuthenticationException $exception
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest($exception->redirectTo() ?? route('login'));
}
This is how Laravel do expectsJson in \Illuminate\Http\Concerns\InteractsWithContentTypes.php
/**
* Determine if the current request probably expects a JSON response.
*
* #return bool
*/
public function expectsJson()
{
return ($this->ajax() && ! $this->pjax() && $this->acceptsAnyContentType()) || $this->wantsJson();
}
/**
* Determine if the current request is asking for JSON.
*
* #return bool
*/
public function wantsJson()
{
$acceptable = $this->getAcceptableContentTypes();
return isset($acceptable[0]) && Str::contains($acceptable[0], ['/json', '+json']);
}
So note that content type header of request is useless. The header useful is "Accept: application/json"
Make sure you are sending correct headers
Accept application/json
Also, you can create your own Middleware for this.

Exceptions will not render Laravel 5.7

I am using Laravel v 5.7.15.
I wrote a validation helper which validates an API request - this works successfully, and prior I was using a try/catch to surround it.
I have moved on to handling the exception in the handler, however I cannot get the function 'render' to run - it goes straight into 'report' and throws in the exception in my tinker console.
Handler: (full class as requested)
<?php
namespace App\Exceptions;
use Illuminate\Validation\ValidationException;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use App\Log;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* #param Exception $exception
* #return mixed|void
* #throws Exception
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
dd($exception);
$log = new Log();
$log->status = 2;
// Validate API incoming data
if ($exception instanceOf ValidationException) {
foreach ($exception->errors() as $error) {
// collect multiple validation errors
$message[] = implode('', $error);
}
$message = implode('', $message);
$log->message = $message;
$log->save();
$response = [
'message' => $message,
'status' => 400,
];
} else {
$response = [
'message' => $exception->getMessage(),
'status' => '500',
];
}
return parent::render($request, $exception);
}
}
This fails to die and dump, however I can dd in the report function and this works fine. The rest of this file has been left untouched, save for the includes at the top of the file.
This is how I call my validator in my controller:
$this->validate($request, BlueparkValidatorArrays::$getOrders);
If anybody could point me in the right direction, I would be most grateful.
This may be caused by a problem in your log configuration.
The call to parent::report($exception); runs the following from the laravel source code:
public function report(Exception $e)
{
...
try {
$logger = $this->container->make(LoggerInterface::class);
} catch (Exception $ex) {
throw $e; // throw the original exception
}
...
}
Note throw $e not throw $ex. So if creating the logger implementation fails, the original exception that was being processed is thrown.
To test this, comment out parent::report($exception); in your report function and see if render() is called as expected.
If it is, your log configuration is not working. Make sure you have the correct permissions on your log location and that your .env file doesn't override any of laravel's logging settings. See How to debug Laravel error 500 with no logs, no information

Laravel 5.4 Show custom error message in login page

I modify the login function in Login controller using credentials function
protected function credentials(\Illuminate\Http\Request $request)
{
return ['email' => $request->email, 'password' => $request->password, 'status' => 1];
}
although the function is work, but i need to return an error message to show that "Account is suspended" in login page if user's status not equal to 1.
How can i modify the error message?
You should make a middleware for that so you can use it. not only in your login function.
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class CheckStatus
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::user()->status != 1) {
return redirect('yourloginpageroute')->withInfo("Account is suspended");
}
return $next($request);
}
}
Assuming that you have a status row in your auth.

Resources