Laravel: workaround the CSRF token via Ajax issue : - laravel-5

Scenario:
I want via ajax send chosen words to a Controller, but I am getting all the time "Internal server error" After a full Sunday of struggling and swearing I think I know why this is happening and how it could be solved. I dont have that problem if I send the word via a ordinary Form and Submit button. The issue is the mis-marriage between Ajax and the CSRF token mismatch.
So here is the Ajax snippet>
<script>
$(document).ready(function(){
$('.choose-language').on('click', function(e){
e.preventDefault();
var selectedlanguage = $(this).data('value');
alert(selectedlanguage); // it gets the value alright on clicking the paragraph
$.ajax({ // so I want to send it to the controller
type:"POST", // via post
url: 'language', // correct?
data:{'locale': selectedlanguage},
}); // HERE FINISHES THE $.POST STUFF
}); //HERE FINISHES THE CLICK FUNCTION
}); // HERE FINISHES THE DOCUMENT AND READY STUFF
</script>
Here is the HTML
<div class="choose-language">
<p class="choose-language" id="english" data-value="en" >English</p>
<p class="choose-language" id="spanish" data-value="es" >Spanish</p>
</div>
Here is the Routes:
Route::get('/', function () {
return view('welcome');
});
Route::post('language', array(
'as' =>'language',
'uses' => 'LanguageController#changelanguage'
));
And the Controller
class LanguageController extends Controller
{
public function changelanguage()
{
Session::set('locale', \Input::get('locale'));
return \Redirect::back();
}
}
So, if I go to Middleware, I can see there is a File called VerifyCSRFToken.php and inside that file there is this:
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
// code here
];
}
So, I am sure that should fix it, but I wrote 'language' where the // code here is and did not make any difference. There must be other bugs..
Thanks a lot.
UPDATE:
I have found a typo (apologies I had written redirecto instead of redirect) and I m not getting errors anymore.

Add the CSRF token to your HTML head:
<meta name="csrf-token" content="<?= csrf_token() ?>">
Add this to your JS file:
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
}
});
The CSRF should now pass the middleware

There was one annoying thing in this HMTL code: as you see the class "choose-language" was repeated also in the div, which caused the click to be repeated twice, and the second time without actually sending a value. So I have left it there for you to see, but you do need to remove it. Once you remove it from the div, the code works correctly. So this responds and solves the issue of Ajax and Laravel CSRF. I have tested the output of the controller and it gets the word sent. Before Laravel 5.0 you had to go through a lot of hacking in the code and fix the bugs and add also conditionals in the filter to let decide when CSRF was or not coming from an Ajax, besides having to add code in every header of every page where you had Ajax sending something etc.
Now, in Laravel 5.0 you just do as I wrote in the code and you are done.

Related

Mews Captcha validation doesn't work when the form is embedded in an iframe

I'm trying to add a captcha to a form, everything works perfectly.
The moment I add that form to an iFrame I start getting a validation error from the captcha.
I'm using Laravel 7.3 and mews/captcha 3.2
the error I get:
Illuminate\Validation\ValidationException: The given data was invalid. in /var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Validator.php:452
HTML code:
<span id="captchaspan"> {!! captcha_img('flat') !!}</span>
<button type="button" class="btn btn-success" id="refresh"><i class="fa fa-refresh" ></i></button>
javascript code:
<script type="text/javascript">
$('.btn-success').click(function(){
$.ajax({
type:'GET',
url:'/refresh-captcha',
datatype:'json',
success: function(data){
$(".captcha span").html(data.captcha);
}
});
});
</script>
and in the controller:
try {
request()->validate(['captcha' => 'required|captcha']);
}catch (\Exception $e){
file_put_contents('log.log', ' captcha problem '.$e,8);
return back()->with('error', 'Captcha Error');
}
refresh Captcha method
public function refreshCaptcha()
{
return response()->json(['captcha'=> captcha_img('flat')]);
}
Important: the problem occurs only when the form is embedded in an iframe.
I'm not sure if this can help, but I had a problem with this library, after hours of trying to figure out the problem and analyzing it, the problem with the library itself, not well tested, anyways my problem was in the config/mews php captcha file there was a property that was called math, I just disabled it, and the check works perfectly now, I hope this can help anyone in the future :)
and if the captcha rule doesn't work try this:
'captcha' => 'required|' , new captchaRule()
along with a custom rule called captchaRule() that simply passes this test:
return captcha_check($value);
for validation you should use captcha_check() function in your controller. pass the submitted captcha string to this function and get a boolean response for validation. like this :
if ( captcha_check($request->captcha) == false ) {
return back()->with('invalid-captcha','incorrect captcha!')
}

Minimum Working Example for ajax POST in Laravel 5.3

Can someone please explain the ajax post method in Laravel 5.3 with a full-working minimum example?
I know there are some resources in the web, but I miss a concise, straight-forward minimum example.
I presume you have a basic understanding of the model-controler-view paradigm, a basic understanding of Laravel and a basic understanding of JavaScript and JQuery (which I will use for reasons of simplicity).
We will create an edit field and a button which posts to the server. (This works for all versions from Laravel 5.0 to 5.6)
1. The Routes
At first you need to add routes to your routes/web.php. Create one route for the view, just as you know from ordinary views:
Route::get('ajax', function(){ return view('ajax'); });
The second route you need to create is the route that handles the ajax post request. Take notice that it is using the post method:
Route::post('/postajax','AjaxController#post');
2. The Controller Function
In the (second) route you created just now, the Controller function post in the AjaxController is called. So create the Controller
php artisan make:controller AjaxController
and in the app/Http/Controllers/AjaxController.php add the function post containing the following lines:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AjaxController extends Controller {
public function post(Request $request){
$response = array(
'status' => 'success',
'msg' => $request->message,
);
return response()->json($response);
}
}
The function is ready to receive data via a Http request and returns a json-formatted response (which consists of the status 'success' and the message the function got from the request).
3. The View
In the first step we defined the route pointing to the view ajax, so now create the view ajax.blade.php.
<!DOCTYPE html>
<html>
<head>
<!-- load jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<!-- provide the csrf token -->
<meta name="csrf-token" content="{{ csrf_token() }}" />
<script>
$(document).ready(function(){
var CSRF_TOKEN = $('meta[name="csrf-token"]').attr('content');
$(".postbutton").click(function(){
$.ajax({
/* the route pointing to the post function */
url: '/postajax',
type: 'POST',
/* send the csrf-token and the input to the controller */
data: {_token: CSRF_TOKEN, message:$(".getinfo").val()},
dataType: 'JSON',
/* remind that 'data' is the response of the AjaxController */
success: function (data) {
$(".writeinfo").append(data.msg);
}
});
});
});
</script>
</head>
<body>
<input class="getinfo"></input>
<button class="postbutton">Post via ajax!</button>
<div class="writeinfo"></div>
</body>
</html>
If you wonder what's the matter with this csrf-token, read https://laravel.com/docs/5.3/csrf

Insert Data Using Ajax

I am using Laravel 5.3. I want to insert the data using blade template.But my when i press submit button it gets refreshed every time. what to do? and please anyone tell me how to use ajax url,type,data
If you try to submit via Javascript make sure prevent form default action with e.preventDefault(). This code prevent the form submitted in a regular way. Just add this code to wrap your AJAX call:
$('#form-id').submit(function(e){
e.preventDefault();
$.ajax({...});
});
I just assume you are using jquery if you are talking about ajax. It's really simple. Your laravel routes listen to "post", "get", "patch", "delete" methods.
Everything of these can be created with a ajax request - example:
$.ajax({
method: "POST",
url: "/posts",
data: { title: "Hello World", text: "..." }
})
.done(function( post ) {
// assuming you return the post
alert(post.title + " created");
});
Now that you use ajax you will not want to return a view to the ajax call. You have different options here (create a new route, helper functions etc.) I will give the most easy example
Controller function:
public function store(Request $request) {
$post = App\Post::create($request->all());
if($request->ajax()) {
return $post;
} else {
return redirect('/posts');
}
}
now you controller will return data on ajax calls and will redirect you on default calls without ajax.
Finally you have a last thing to keep in mind. If you have web middleware applied ( done by default ) you need to handle the csrf token. The most easy way to handle this is by adding a meta tag to your html head
and then (before doing all your calls etc.) add this to configure your ajax
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')
}
});
this will add the valid csrf token which is in your head to every ajax call and will ensure you not run into token missmatch exceptions.
Things to keep in mind:
- if you stay very long on one page tokens might expire ( laravel-caffeine will help here )
- you need to handle validation for ajax calls

Laravel: controller not teletransporting me (redirect-ing me) to the page

from Ajax the controller does get the keyword I want, as it confirms it (because I echo it), and my idea was that on getting that keyword, it should redirect to the page I want. Yet, it does not, and also, while it does change the locale, I have to reload the page, otherwise, it won't show any translation and locale changes on the page. In Firebug when I hover over the POST, I get the correct URL to where I would want to go: sort of http://myweb.com/es but the controller does not change the http URL box of my browser on my web to go there.
I am simplifying the Controller code here, but actually I will want it to go to different URLs depending on the keyword it gets, something that I would do with a switch statement or IF else if etc.
So the controller is as simple as this:
public function changelanguage()
{
$lang = \Input::get('locale');
echo "I got $lang";
Session::put('locale', $lang);
return redirect('/es');
}
If instead of using ajax I use a Form, then I dont need to reload, the Action of the form makes the controller change the locale and translate the page without reloading. But I need to use ajax and in any case, the controller does get correctly the keyword ('en', 'es', 'de' etc ) for languages, so it should take it from there and redirect me to the URL page, but it just doesnt move.
if you are curious about the Ajax, here it is, but it does send the keyword as I said.
$(document).ready(function(){
$('.choose-language').on('click', function(e){
e.preventDefault();
var selectedlanguage = $(this).data('value');
$.ajax({ // so I want to send it to the controller
type:"POST", // via post
url: 'language',
data:{'locale': selectedlanguage},
}); // HERE FINISHES THE $.POST STUFF
}); //HERE FINISHES THE CLICK FUNCTION
}); // HERE FINISHES THE CODE
ROUTES
Route::post('language', array(
'as' =>'language',
'uses' => 'LanguageController#changelanguage'
));
If you’re trying to perform the redirect in the AJAX-requested script, then it won’t work. You can’t redirect from a script request via AJAX otherwise people would be doing all kinds of nefarious redirects.
Instead, set up a “success” handler on your AJAX request that refreshes your page if the request was successful. It can be as simple as:
var url = '/language';
var data = {
locale: $(this).data('value');
};
var request = $.post(url, data)
.success(function (response) {
// Script was successful; reload page
location.reload();
});
I’m not sure how you’re allowing users to select locales, but since you need a reload any way I think AJAX is pointless here. Just have a traditional form that submits the new locale to an action, set the locale in a session/cookie/whatever, and then redirect back to the referring page.

Laravel 4.0 Filter and Ajax to load a bootstrap modal pop up

I'm doing a website based laravel 4.2 and Bootstrap. I want a bootstrap modal pop up dialog to load on certain routes provided that the user is not already logged in.
So far i have accomplsihed it to load for the entire application upon loading which is not i want but am in the right direction.
This is my ajax call
script type="text/javascript"> $(document).ready(function() {
$.get("status", function(data, status){
data.status == false ? $('#modal-form').modal({backdrop: 'static'}) : $('#modal-form').modal('hide');
});
});
Status refers to a URL defined in this route
Route::get('/status', 'LoginController#getLoginStatus');
and the method is defined here
public function getLoginStatus()
{
return Response::json(array( 'status' => Sentry::check()));
}
From that, the modal dialog loads on each route across the entire application. I would want to limit the dialog to load on certain routes provided the user is not logged in.
Thing Laravel filter would do the trick for me but i have failed to do so.
Something like
Route::filter('status', function()
{
});
and then the route be like:
Route::get('profile', array('as' => 'submit-profile','before' => 'status','uses' => 'ProfileController#getProfile'));
Thanks guys hope you can give me some advice.
You could use the
Route::currentRouteName()
to check what route your on and then do your preferred action,
like calling the ajax get request for the modal.

Resources