Exception Handling when using both web and token api gaurd - laravel

Using laravel5.8. Using both web and API(Token Gaurd).
when using api call with invalid api_token parameter receiving an error Route:Login not defined. I want the response in JSON. Read in Forum I need to use the below way in app\Exceptions\Handler.php and it works. I have web gaurd for some of the paths. I want the route:login to work when its a web gaurd and return json response when using api gaurd. How can I do it in Laravel 5.8?
public function render($request, Exception $exception)
{
// dd(get_class($exception));
// return parent::render($request, $exception);
return response()->json(
[
'errors' => [
'status' => 401,
'message' => 'Unauthenticated',
]
], 401
);
}

I put the logic in unauthenticated function, combines with expectsJson() should solve your problem
// in app\Exceptions\Handler.php
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['status' => 401,'message' => 'Unauthenticated.'], 401);
}
return redirect()->guest('/');
}

Related

laravel MethodNotAllowedHttpException redirect to 404

I use laravel 8 tried to edit my exceptions\handler.php
public function render($request, Throwable $exception)
{
if ($exception instanceof MethodNotAllowedHttpException) {
abort(404);
}
return parent::render($request, $exception);
}
but his gives not 404 but 500 when checking routes where MethodNotAllowedHttpException
One possible solution is to supply your routes/web.php file with a Route fallback. Try adding the following to the bottom of your web routes:
Route::fallback( function () {
abort( 404 );
} );
on Laravel 8 override default handler render function on App\Exceptions\Handler.php
public function render($request, Throwable $exception)
{
if ($exception instanceof MethodNotAllowedHttpException) {
if ($request->ajax() || $request->wantsJson() || $request->expectsJson()) {
//405 for Method Not Allowed
return response()->json(['error' => 'Bad Request'], 405);
}
else {
return parent::render($request, $exception);
}
}
}
don't forget to add use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;to test in Postman set key=>Accept ,value => application/json to test handler
reff: Return a 404 when wrong ttp request type used(e.g. get instead of post) in laravel

Return a 404 when wrong ttp request type used(e.g. get instead of post) in laravel

I am developing a rest API and sometimes the frontend call an endpoint with the wrong HTTP request type. For example, I have a route (/users/unassigned) and the type of the route is "GET". Imagin my frontend call this route with a "POST" request. the following error occurs.
The POST method is not supported for this route. Supported methods: GET, HEAD, PUT, DELETE.
What I want is that the API response a JSON in these situations and I can handle the exception.I have used the Route::fallback but this method catch every exception of the routes. I need a function that only handles the told problem.
you can make some adjustment in your exception handler.
in app/Exceptions/Handler.php file's render function
public function render($request, Exception $exception)
{
if ($exception instanceof MethodNotAllowedHttpException) {
if ($request->ajax()) {
return response()->json(['error' => 'Route Not Found'], 404);
}
else {
//something else
}
}
return parent::render($request, $exception);
}
and add use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; at the top use section.
One way is to handle it with app/Exceptions/Handler.php:
public function render($request, Exception $e)
{
if ($request->ajax() || $request->wantsJson()) {
return response()->json([
'error' => $e->getMessage(),
], 400);
}
return parent::render($request, $e);
}
This will ouput the error message as json for all exceptions when the request is made by ajax or the request expect json.
You can also add a check for type of exception like this:
if ($exception instanceof MethodNotAllowedHttpException) {
// Code...
}
Thank you so much.
I use the following code in my handler.php.
{
if ($this->isHttpException($exception)) {
switch ($exception->getStatusCode()) {
// not authorized
case '403':
return Response()->json(["error" => "not authorized."],403);
break;
// not found
case '404':
return Response()->json(["error" => "Route not found."],404);
break;
// internal error
case '500':
return Response()->json(["error" => "internal error."],500);
break;
case '405':
return Response()->json(["error" => "request type not match."],405);
break;
default:
return $this->renderHttpException($exception);
break;
}
}
else {
return parent::render($request, $exception);
}
}
The main point is http code 405 for not allowed methodes. special thanks to #apokryfos.
Check what type of request you are getting in your $request variable, then handle it with condition.
change in your Handler.php
like
protected $dontReport = [
\Illuminate\Auth\AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
\Illuminate\Session\TokenMismatchException::class,
\Illuminate\Validation\ValidationException::class,
//
];
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
$arr = array("status" => 400, "message" =>"Unauthorized access", "data" => array());
return \Response::json($arr);
}
return redirect()->guest('login');
}

Laravel exception if api return json, if website return default page

public function render($request, Exception $exception)
{
if ( $request->route()->getPrefix() == 'api' ) {
some code......
return response()->json($response, $status);
}
return parent::render($request, $exception);
}
I have a website content site & api both.
I want my exception return json when user request api
If user request is not api, it will return the default page.
But when I put logic into function render, the default one got error.
Anyone know how to fix this?
I'm not quite sure what your question is. But you can detect if you should return a JSON response or not using the wantsJson() method. For example the code should instead read to say:
public function render($request, Exception $exception)
{
if ( $request->wantsJson()) {
some code......
return response()->json($response, $status);
}
return parent::render($request, $exception);
}
Laravel should automatically detect this and return a JSON response for exceptions if you are making a JSON request. The fact it isn't doing this, I would check to make sure you are making a JSON request and not a standard HTTP request.
Use $request->wantsJson() and check if the route is prefixed with api:
public function render($request, Exception $exception)
{
if ( $request->wantsJson() && $request->is('api/*')) {
some code......
return response()->json($response, $status);
}
return parent::render($request, $exception);
}
See here for wantsJson() method, and other useful Request methods: https://laravel.com/api/5.7/Illuminate/Http/Request.html#method_wantsJson

How to catch all exceptions in Laravel

I was wondering if there is a way to catch all the exceptions that are thrown in a laravel app and store them in database ?
I have been looking at some packages but coudn't find anything that tells where and how to catch the exceptions.
for catch all errors and log them you need to edit App\Exceptions\Handler file like this
public function render($request, Exception $exception)
{
if ($exception){
// log the error
return response()->json([
'status' => $exception->getStatusCode(),
'error' => $exception->getMessage()
]);
}
return parent::render($request, $exception);
}
As stated in the Docs,
You need to have to customize the render() method of App\Exceptions\Handler.
Edit the app/Exceptions/Handler.php:
public function render($request, Exception $e)
{
$error =$e->getMessage();
//do your stuff with the error message
return parent::render($request, $exception);
}

Different Response(JSON and webpage) in API and Website for Laravel 404 and 500?

I want to show the different response for API and website. In api response I want to show json response with 404 and 500 for type of exception mainly for routes.
If a user try to request a route and route not found I want to show a response in json response for API and webpage for website.
I know and try the code into app/Exceptions/Handler.php
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
if ($request->expectsJson()) {
return response()->json(['error' => 'Not Found'], 404);
}
return response()->view('404', [], 404);
}
return parent::render($request, $exception);
}
https://laravel.com/docs/5.4/errors#http-exceptions
but failed can anybody help me how can I set different responses for error pages.
Expects JSON is about headers, i do not like that solution for API errors, you can access the API through a browser. My solution is most of the times to filter by the URL route, because it starts with "api/...", which can be done like so $request->is('api/*').
If you have your routes that are not prefixes with /api, then this will not work. Change the logic to fit with your own structure.
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
if ($request->is('api/*')) {
return response()->json(['error' => 'Not Found'], 404);
}
return response()->view('404', [], 404);
}
return parent::render($request, $exception);
}
Just wanted to add an alternative to the above answers, which all seem to work as well.
After having the same problem and digging deeper, I took a slightly different approach:
your exception handle calls parent::render(...). If you look into that function, it will render a json response if your request indicates that it wantsJson() [see hints how that works here]
now, to turn all responses (including exceptions) to json I used the Jsonify Middleware idea from here, but applied it to the api MiddlewareGroup, which is by default assigned to RouteServiceProvider::mapApiRoutes()
Here is one way to implement it (very similar to referenced answer from above):
Create the file app/Http/Middleware/Jsonify.php
<?php
namespace App\Http\Middleware;
use Closure;
class Jsonify
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ( $request->is('api/*') ) {
$request->headers->set('Accept', 'application/json');
}
return $next($request);
}
}
Add the middleware reference to your $routeMiddleware table of your app/Http/Kernel.php file:
protected $routeMiddleware = [
...
'jsonify' => \App\Http\Middleware\Jsonify::class,
...
];
In that same Kernel file, add the jsonify name to the api group:
protected $middlewareGroups = [
...
'api' => [
'jsonify',
'throttle:60,1',
'bindings',
],
...
];
Result is that the middleware gets loaded for any requests that fall into the 'api' group. If the request url begins with api/ (which is slightly redundant I think) then the header gets added by the Jsonify Middleware. This will tell the ExceptionHandler::render() that we want a json output.
No need to hustle again for Laravel upgrade. You just need to define this method in the routes/api.php
Route::fallback(function(){
return response()->json(['message' => 'Not Found!'], 404);
});
I'm using Laravel 5.5.28, and am adding this in app/Exceptions/Handler.php
public function render($request, Exception $exception)
{
// Give detailed stacktrace error info if APP_DEBUG is true in the .env
if ($request->wantsJson()) {
// Return reasonable response if trying to, for instance, delete nonexistent resource id.
if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
return response()->json(['data' => 'Resource not found'], 404);
}
if ($_ENV['APP_DEBUG'] == 'false') {
return response()->json(['error' => 'Unknown error'], 400);
}
}
return parent::render($request, $exception);
}
This expects that your API calls will be having a header with key Accept and value application/json.
Then a nonexistent web route returns the expected
Sorry, the page you are looking for could not be found
and a nonexistent API resource returns a JSON 404 payload.
Found the info here.
You could combine this with the answer looking for the instance of NotFoundHttpException to catch the 500. I imagine, however, that the stack trace would be preferred.
finally found this for 9.x
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Register the exception handling callbacks for the application.
*
* #return void
*/
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}
source: https://laravel.com/docs/9.x/errors#rendering-exceptions
try this.
public function render($request, Exception $exception)
{
if ($request->ajax()) {
return \Response::json([
'success' => false,
'message' => $exception->getMessage(),
], $exception->getCode());
} else {
return parent::render($request, $exception);
}
}

Resources