how to change "CSRF token mismatch" message? - ajax

I'm using larvel 8 and want to change message of "CSRF token mismatch" when using ajax post. I created a closure and passed it to the renderable method on the App\Exceptions\Handler class, but the previews message appears.
This is my code:
use Illuminate\Session\TokenMismatchException;
class Handler extends ExceptionHandler
{
protected $dontReport = [
//
];
protected $dontFlash = [
'password',
'password_confirmation',
];
public function register()
{
$this->renderable(function (TokenMismatchException $e, $request) {
return $request->expectsJson()
? response()->json(['message' => 'A new message...'], 419)
: redirect()->guest(route('login'));
});
}

To modify error message on TokenMismatchException both for web-pages and ajax-requests
It would be better to overload prepareException() method in application exception handler:
protected function prepareException(Exception $e)
{
if ($e instanceof TokenMismatchException) {
$e = new HttpException(419, __('exception.csrf_token_mismatch'), $e);
}
return parent::prepareException($e);
}
So you can create translation file and modify message by language files. For example create resources/lang/en/exception.php with content below:
<?php
return [
'csrf_token_mismatch' => 'CSRF token mismatch. Please, refresh page (CTRL+R) and try again.',
];

thanks to everyone who contributed, I found the solution.
Due to laravel change the TokenMismatchException to HttpException in the function prepareException in the Illuminate\Foundation\Exceptions\Handler class(parent of Handler class), we cannot render the TokenMismatchException.
protected function prepareException(Throwable $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
} elseif ($e instanceof AuthorizationException) {
$e = new AccessDeniedHttpException($e->getMessage(), $e);
} elseif ($e instanceof TokenMismatchException) {
$e = new HttpException(419, $e->getMessage(), $e);
} elseif ($e instanceof SuspiciousOperationException) {
$e = new NotFoundHttpException('Bad hostname provided.', $e);
} elseif ($e instanceof RecordsNotFoundException) {
$e = new NotFoundHttpException('Not found.', $e);
}
return $e;
}
I modify my renderable method as below and now I can catch the TokenMismatchException:
$this->renderable(function (HttpException $e, $request) {
if ($e->getPrevious() instanceof TokenMismatchException) {
return $request->expectsJson()
? response()->json(['message' =>'Your new message ...', 419)
: redirect()->guest(route('login'));
}
});

If you want to change the error message or the page that is shown when CSRF token mismatch happen
Run this command: php artisan vendor:publish --tag=laravel-errors
It will publish your default (vendor) exceptions page to resources/views/errors/
From there, edit resources/views/errors/419.blade.php with html that you would like to show when CSRF verification error happen.
References: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
419 Page Expired (Laravel Framework)
Used by the Laravel Framework when a CSRF Token is missing or expired.
If you want to allow ajax requests to bypass CSRF token verification
Reference: https://laravel.com/docs/8.x/csrf#csrf-excluding-uris
Edit your VerifyCsrfToken middleware (location: app/Http/Middleware/VerifyCsrfToken.php), add:
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}

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

Only load route with existent parametre

Im doing a crud, i want to show item data using id, i have this in web.php:
Route::get('update/{id}', 'CrudController#update');
How can I deny that the user changes the id in the path to one that does not exist? That shows only those that exist and those that do not, that do not load?
In your update method, you can do the following:
public function update($id)
{
MyModel::findOrFail($id);
//...perform other actions
}
It will throw a 404 response if the requested $id is a non-existent one.
Then you can catch it if you want in the render() method of app\Exceptions\Handler.php:
use Illuminate\Database\Eloquent\ModelNotFoundException;
.
.
.
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException) {
if ($request->wantsJson()) {
return response()->json([
'data' => 'Resource not found'
], 404);
} else {
abort(404);
}
}
return parent::render($request, $exception);
}
Or, If you do not want to go through all the trouble of configuring it in the handler, you could also do:
public function update($id)
{
if (! $model = MyModel::find($id)) {
abort(404);
}
//...perform other actions with $model
}
The abort(404) method takes the user to the default Page not found page of laravel, which is an appropriate thing to do.

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);
}
}

How to handle TokenMismatchException

I want to handle TokenMismatchException in VerifyCsrfToken.php line 67: exception and then redirect user to login page.
The error occurs when user is logged and and clear browser data (session, history etc) or the user's session expires.
I've already tried the following solution in Handler.php:
if ($e instanceof TokenMismatchException) {
return redirect()->route('welcome');
}
Full Haler.php code:
class Handler extends ExceptionHandler {
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
public function report(Exception $e)
{
parent::report($e);
}
public function render($request, Exception $e)
{
if ($e instanceof TokenMismatchException) {
return redirect()->route('welcome');
}
return parent::render($request, $e);
}
}
I've also tried to return the status code and then try to handle it, but when I return status code by $e->getStatusCode() it does not return any value. Therefore I don't know how to handle this exception.
Any reasonable suggestions will be appreciated.
If you want to handle the TokenMismatchException from your app/Exceptions/Handlerthen you need to remove it from the $dontReport array in your Handler class
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
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, --> delete this line
\Illuminate\Validation\ValidationException::class,
];
Although, I would warn you that redirecting the user to the login page on this exception isn't really the right way to handle this.
A TokenMismatchException is thrown when the csrf token doesn't match. CSRF protection is for both logged in users and guests. So, redirecting to the login page is not really a solution to this problem.

How to return AJAX errors from a Laravel controller?

I am building a REST API with Laravel 5.
In Laravel 5, you can subclass App\Http\Requests\Request to define the validation rules that must be satisfied before a particular route will be processed. For example:
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class BookStoreRequest extends Request {
public function authorize() {
return true;
}
public function rules() {
return [
'title' => 'required',
'author_id' => 'required'
];
}
}
If a client loads the corresponding route via an AJAX request, and BookStoreRequest finds that the request doesn't satisfy the rules, it will automagically return the error(s) as a JSON object. For example:
{
"title": [
"The title field is required."
]
}
However, the Request::rules() method can only validate input—and even if the input is valid, other kinds of errors could arise after the request has already been accepted and handed off to the controller. For example, let's say that the controller needs to write the new book information to a file for some reason—but the file can't be opened:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Requests\BookCreateRequest;
class BookController extends Controller {
public function store( BookStoreRequest $request ) {
$file = fopen( '/path/to/some/file.txt', 'a' );
// test to make sure we got a good file handle
if ( false === $file ) {
// HOW CAN I RETURN AN ERROR FROM HERE?
}
fwrite( $file, 'book info goes here' );
fclose( $file );
// inform the browser of success
return response()->json( true );
}
}
Obviously, I could just die(), but that's super ugly. I would prefer to return my error message in the same format as the validation errors. Like this:
{
"myErrorKey": [
"A filesystem error occurred on the server. Please contact your administrator."
]
}
I could construct my own JSON object and return that—but surely Laravel supports this natively.
What's the best / cleanest way to do this? Or is there a better way to return runtime (as opposed to validate-time) errors from a Laravel REST API?
You can set the status code in your json response as below:
return Response::json(['error' => 'Error msg'], 404); // Status code here
Or just by using the helper function:
return response()->json(['error' => 'Error msg'], 404); // Status code here
You can do it in many ways.
First, you can use the simple response()->json() by providing a status code:
return response()->json( /** response **/, 401 );
Or, in a more complexe way to ensure that every error is a json response, you can set up an exception handler to catch a special exception and return json.
Open App\Exceptions\Handler and do the following:
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
NotFoundHttpException::class,
// Don't report MyCustomException, it's only for returning son errors.
MyCustomException::class
];
public function render($request, Exception $e)
{
// This is a generic response. You can the check the logs for the exceptions
$code = 500;
$data = [
"error" => "We couldn't hadle this request. Please contact support."
];
if($e instanceof MyCustomException) {
$code = $e->getStatusCode();
$data = $e->getData();
}
return response()->json($data, $code);
}
}
This will return a json for any exception thrown in the application.
Now, we create MyCustomException, for example in app/Exceptions:
class MyCustomException extends Exception {
protected $data;
protected $code;
public static function error($data, $code = 500)
{
$e = new self;
$e->setData($data);
$e->setStatusCode($code);
throw $e;
}
public function setStatusCode($code)
{
$this->code = $code;
}
public function setData($data)
{
$this->data = $data;
}
public function getStatusCode()
{
return $this->code;
}
public function getData()
{
return $this->data;
}
}
We can now just use MyCustomException or any exception extending MyCustomException to return a json error.
public function store( BookStoreRequest $request ) {
$file = fopen( '/path/to/some/file.txt', 'a' );
// test to make sure we got a good file handle
if ( false === $file ) {
MyCustomException::error(['error' => 'could not open the file, check permissions.'], 403);
}
fwrite( $file, 'book info goes here' );
fclose( $file );
// inform the browser of success
return response()->json( true );
}
Now, not only exceptions thrown via MyCustomException will return a json error, but any other exception thrown in general.
A simple approach is to use the abort() method in the controller. This will return an error that will be picked up by ajax error:function(){}
Controller Example
public function boost_reputation(Request $request){
$page_owner = User::where('id', $request->page_owner_id)->first();
// user needs to login to boost reputation
if(!Auth::user()){
toast('Sorry, you need to login first.','info');
abort();
}
// page owner cannot boost his own reputation
if(Auth::user() == $page_owner){
toast("Sorry, you can't boost your own reputation.",'info');
abort();
}
}
Ajax Example
$('.reputation-btn').on('click',function(event){
var btn = this;
var route = "{{ route('boost_reputation') }}";
var csrf_token = '{{ csrf_token() }}';
var id = '{{ $page_owner->id }}';
$.ajax({
method: 'POST',
url: route,
data: {page_owner_id:id, _token:csrf_token},
success:function(data) {
...your success code
},
error: function () {
...your error code
}
});
});
More info: https://laravel.com/docs/7.x/errors

Resources