This question already has answers here:
jQuery add CSRF token to all $.post() requests' data
(7 answers)
Closed 4 years ago.
I have an AJAX request which is a GET request.
/**
* AJAX Like function
*/
$(".like").click(function (e) {
e.preventDefault(); // you dont want your anchor to redirect so prevent it
$.ajax({
type: "GET",
// blade.php already loaded with contents we need, so we just need to
// select the anchor attribute href with js.
url: $('.like').attr('href'),
success: function () {
if ($('.like').hasClass('liked')) {
$(".like").removeClass("liked");
$(".like").addClass("unliked");
$('.like').attr('title', 'Like this');
} else {
$(".like").removeClass("unliked");
$(".like").addClass("liked");
$('.like').attr('title', 'Unlike this');
}
}
});
});
Where the URL is: http://127.0.0.1:8000/like/article/145
And is grabbed via the href attribute of .like, the markup of which looks like this:
<div class="interaction-item">
#if($article->isLiked)
<a href="{{ action('LikeController#likeArticle', $article->id) }}" class="interactor like liked" role="button" tabindex="0" title="Unlike this">
#else
<a href="{{ action('LikeController#likeArticle', $article->id) }}" class="interactor like unliked" role="button" tabindex="0" title="Like this">
#endif
<div class="icon-block">
<i class="fas fa-heart"></i>
</div>
</a>
</div>
The LikeController looks like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use App\Like;
use App\Article;
use App\Event;
use Illuminate\Support\Facades\Auth;
class LikeController extends Controller
{
/**
* Display all liked content for this user
*/
public function index()
{
$user = Auth::user();
$articles = $user->likedArticles()->get();
$articleCount = count($articles);
$events = $user->likedEvents()->get();
$eventCount = count($events);
return view('pages.likes.index', compact('articles', 'articleCount', 'events', 'eventCount'));
}
/**
* Handle the liking of an Article
*
* #param int $id
* #return void
*/
public function likeArticle($id)
{
// here you can check if product exists or is valid or whatever
$this->handleLike(Article::class, $id);
return redirect()->back();
}
/**
* Handle the liking of an Event
*
* #param int $id
* #return void
*/
public function likeEvent($id)
{
// here you can check if product exists or is valid or whatever
$this->handleLike(Event::class, $id);
return redirect()->back();
}
/**
* Handle a Like
* First we check the existing Likes as well as the currently soft deleted likes.
* If this Like doesn't exist, we create it using the given fields
*
*
* #param [type] $type
* #param [type] $id
* #return void
*/
public function handleLike($type, $id)
{
$existingLike = Like::withTrashed()
->whereLikeableType($type)
->whereLikeableId($id)
->whereUserUsername(Auth::user()->username)
->first();
if (is_null($existingLike)) {
// This user hasn't liked this thing so we add it
Like::create([
'user_username' => Auth::user()->username,
'likeable_id' => $id,
'likeable_type' => $type,
]);
} else {
// As existingLike was not null we need to effectively un-like this thing
if (is_null($existingLike->deleted_at)) {
$existingLike->delete();
} else {
$existingLike->restore();
}
}
}
}
I think it's extremely bad practice to update a database via a GET request
So, I changed the Route to use POST and updated the AJAX call to:
/**
* AJAX Like function
*/
$(".like").click(function (e) {
e.preventDefault(); // you dont want your anchor to redirect so prevent it
$.ajax({
type: "POST",
// blade.php already loaded with contents we need, so we just need to
// select the anchor attribute href with js.
url: $('.like').attr('href'),
data: {
_token: '{{ csrf_token() }}'
},
success: function () {
if ($('.like').hasClass('liked')) {
$(".like").removeClass("liked");
$(".like").addClass("unliked");
$('.like').attr('title', 'Like this');
} else {
$(".like").removeClass("unliked");
$(".like").addClass("liked");
$('.like').attr('title', 'Unlike this');
}
}
});
});
As you can see, I've changed the method and added in the CSRF token, however, I get an error:
POST http://127.0.0.1:8000/like/article/145 419 (unknown status)
send # app.js:14492
ajax # app.js:14098
(anonymous) # app.js:27608
dispatch # app.js:10075
elemData.handle # app.js:9883
app.js:14492 XHR failed loading: POST "http://127.0.0.1:8000/watch/article/145".
What is the best way to debug what's going on?
Update
By adding: <meta name="csrf-token" content="{{ csrf_token() }}"> would it interfer with my normal use of `#csrf' in my forms?
Also, I added a fail callback to the request
}).fail(function (jqXHR, textStatus, error) {
// Handle error here
console.log(jqXHR.responseText);
});
Another update
As you have all kindly pointed out, in the documentation here, it does indeed say, set the meta CSRF attribute, I even had an error in my console saying this wasn't defined, but I misinterpreted the error.
Why though, in some tutorials, do people add the CSRF token to the data array?
Look at this. You can't use {{ csrf_token() }} in your JS.
Try this in your html :
<meta name="csrf-token" content="{{ csrf_token() }}">
And this in your JS :
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
If you have multiple ajax call in single file then you can do via this. The following code will working for all your AJAX requests.
Try this in your html :
<meta name="csrf-token" content="{{ csrf_token() }}">
And this in your JS :
$(document).ready(function() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
});
Related
Hi guys I'm new to CodeIgniter I'm still figuring out things here. So I have a problem about the ajax request it seem that the controller is not accepting the ajax request. I`m using CodeIgniter 4.0 by the way. I have search a lot of material in the internet and YouTube but still nothing works.
So here's the code in the view folder named layout.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="{csrf_header}" content="{csrf_hash}">
<title>Ajax test</title>
</head>
<body>
<!-- <form method="post" action="ajaxtest"> -->
<div>
<button type="submit" id="test">test Ajax</button>
</div>
<!-- </form> -->
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script type='text/javascript'>
$(document).ready(function() {
//var tokenName = $('#token').attr("name");
//var tokenVal = $("#token").val();
// alert(tokenName)
// alert(tokenVal)
$('#test').on('click', function() {
// alert('ok');
$.ajax({
url: "<?php echo base_url(); ?>/ajaxtest",
data: {
[tokenName]: tokenVal
},
method: "post",
success: function(response) {
alert('ok')
},
error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
});
});
</script>
</body>
</html>
And heres the code in controller
<?php
namespace App\Controllers;
// defined('BASEPATH') or exit('No direct script access allowed');
class AjaxController extends BaseController
{
public function index()
{
if ($this->request->getMethod() == 'post') {
echo 'post request done, ';
if ($this->request->isAJAX()) {
return 'the request is ajax';
} else {
return 'the request is not ajax';
}
}
echo view('layout');
}
}
Here`s the code inside the routes.php
<?php
namespace Config;
// Create a new instance of our RouteCollection class.
$routes = Services::routes();
// Load the system's routing file first, so that the app and ENVIRONMENT
// can override as needed.
if (is_file(SYSTEMPATH . 'Config/Routes.php')) {
require SYSTEMPATH . 'Config/Routes.php';
}
/*
* --------------------------------------------------------------------
* Router Setup
* --------------------------------------------------------------------
*/
$routes->setDefaultNamespace('App\Controllers');
$routes->setDefaultController('Home');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override();
// $routes->setAutoRoute(true);
// The Auto Routing (Legacy) is very dangerous. It is easy to create vulnerable apps
// where controller filters or CSRF protection are bypassed.
// If you don't want to define all routes, please use the Auto Routing (Improved).
// Set `$autoRoutesImproved` to true in `app/Config/Feature.php` and set the following to true.
//$routes->setAutoRoute(false);
/*
* --------------------------------------------------------------------
* Route Definitions
* --------------------------------------------------------------------
*/
// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'Home::index');
$routes->match(['get', 'post'], 'ajaxtest', 'AjaxController::index');
// $routes->get('ajaxtest', 'AjaxController::index');
// $routes->post('ajaxtest/success', 'AjaxController::success');
/*
* --------------------------------------------------------------------
* Additional Routing
* --------------------------------------------------------------------
*
* There will often be times that you need additional routing and you
* need it to be able to override any defaults in this file. Environment
* based routes is one such time. require() additional route files here
* to make that happen.
*
* You will have access to the $routes object within that file without
* needing to reload it.
*/
if (is_file(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
}
Now what Im doing here is just trying to test if ajax works and return something. If I uncomment the form tag in the layout file the request works fine but I need the request given by ajax not in the form tag so I wont use it but if I use the ajax it wont respond anything and there is no error like in ci4 and the jquery. But the weird thing is that when I use post man and send ajax request it works perfectly fine. Can someone point out what Im missing here?
Here is the solution.
Some change in your ajax function.
var value1 = 1;
var value2 = "val2";
$.ajax({
url: "<?php echo base_url(); ?>/ajaxtest",
data: {
"key1": value1,
"key2": value2
}, // data will be set as key value pair
method: "post",
success: function (response) {
consol.log(response); // to view your response from controller in webbrowser's console
alert(response.message); // alret response message
// alert('ok');
},
error: function (xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
Some change in your controller function
public function index() {
$this->load->view('layout');
}
public function ajax()
{
$data = [];
if($this->input->post()){
$data['message'] = 'the request is ajax post';
$data['data'] = $this->input->post(); // get data from ajax function's data object
}else{
$data['message'] = 'the request is not ajax post';
}
return response()->json($data);
}
Some change in your routes.php
// $routes->match(['get', 'post'], 'ajaxtest', 'AjaxController::index');
$routes->get('ajaxtest', 'AjaxController::index');
$routes->post('ajaxtest', 'AjaxController::ajax');
i can't find the problem, i want to update the data to database, but i get Patch method is not support. i can't find the problem, i check from book refrence have not different, but in this case can't run for update.
this is my web.php
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('laravel/data', 'LaravelController#listData')->name('laravel.data');
Route::resource('laravel', 'LaravelController');
this my controller
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$laravel = Laravel::find($id);
$laravel = $request['id'];
$laravel->tanggal_pemasangan = $request['tanggal'];
$laravel->pitstop = $request['pitstop'];
$laravel->merk_ban = $request['merk_ban'];
$laravel->no_series_ban = $request['series_ban'];
$laravel->jenis_mobil = $request['jenis_mobil'];
$laravel->plat_nomor = $request['plat_mobil'];
$laravel->posisi_ban = $request['posisi_ban'];
$laravel->status = $request['status'];
$laravel->update();
}
my form.blade
<form class="form-horizontal" data-toggle="validator" method="POST">
#method('POST')
#csrf
and my ajax
$('#modal-form form').validator().on('submit', function(e){
if(!e.isDefaultPrevented()){
var id = $('#id').val();
if(save_method == "add") url = "{{ route('laravel.store') }}"
else url = "laravel/"+id;
$.ajax({
url : url,
type: "POST",
data: $('#modal-form form').serializa(),
success : function(data){
$('#modal-form form').modal('hide');
table.ajax.reload();
},
error: function(){
alert("Tidak dapat menyimpan data!");
}
});
return false;
}
});
function editForm(id){
save_method = "edit";
$('input[name = _method]').val('PATCH');
$('#modal-form form')[0].reset();
$.ajax({
url : "laravel/"+id+"/edit",
type: "GET",
dataType: "JSON",
success: function(data){
$('#modal-form').modal('show');
$('.modal-title').text('Edit Data');
$('#id').val(data.id);
$('#tanggal').val(data.tanggal_pemasangan);
$('#pitstop').val(data.pitstop);
$('#merk_ban').val(data.merk_ban);
$('#series_ban').val(data.no_series_ban);
$('#jenis_mobil').val(data.jenis_mobil);
$('#plat_mobil').val(data.plat_nomor);
$('#posisi_ban').val(data.posisi_ban);
$('#status').val(data.status);
},
error: function(){
alert("Tidak dapat menampilkan data!");
}
});
}
every time i want to do update, always give me error 405 this method is not support
Verify that your server supports the HTTP request: "PUT".
In your blade.php file add the #method('put') inside the form.
<form method="POST">
#method('PUT')
#csrf
...
</form>
Considering that resource type controllers generate the routes for you, perhaps you should use the php artisan route:list command to check the update route parameters.
Some examples of generated routes for resource type controllers, here.
Now maybe it can be a route hierarchy problem, try changing the order of your routes like this.
Route::resource('laravel', 'LaravelController');
Route::get('laravel/data', 'LaravelController#listData')->name('laravel.data');
Route::resource() #update method uses PATCH|PUT verb instead of POST
In your case you use POST method so the request will be not compatible, You need to use PATCH or PUT instead of POST in your type in ajax or in the form if necessary
I'm new with laravel and I want to send the selected dropdown option value of product name through ajax data to the controller
For Example: If I'm select 1st plastic product option value from a drop-down then in the controller from request object I want that selected product name
as per my below code I'm getting null in the request object of the product name
Here is my route:
Route::get('product', 'ProductController#index')->name('product');
Here is my controller:
public function index(Request $request)
{
if (isset($request->productName)) {
$productName = $request->productName;
dump($productName); // getting null
} else {
$productName = null;
}
return view('Product.product');
}
Here is my an ajax call:
function display(productName){
productName = $('#product_filter').val(); // Here, I'm getting selected value of dropdown
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url: "{{route('product')}}",
type: "GET",
data:{
'productName' : productName // in header request I'm getting value [productName: plastic product] *
},
success:function(data){
console.log(data);
},
error:function(e){
console.log(e,'error');
}
});
}
header request result
I don't know if I'm doing something wrong,
ends with wanting to get help from helping hands please help me to get the selected value to the controller object
I believe you got null because you are returning a full HTML to the Ajax request. In order to get the payload sent from the Ajax, you have to return a JSON response like this:
public function index(Request $request)
{
$productName = null;
if (isset($request->productName)) {
$productName = $request->productName;
}
return response()->json($productName);
}
That being said, I'm unable to reproduce the issue without seeing how do you call the method and where would you show the data to. And I assume you want to simply just do a console.log(data) like you did on the given snippet. In this case, the snippet above will work.
And if you want to keep the view to prevent error when you refresh the page, just add a new method for that specific call in your controller and send the request to that endpoint, like this:
web.php
<?php
Route::get('/', [ProductController::class, 'index']);
Route::get('/productFilter', [ProductController::class, 'productFilter'])->name('dashboard-product-data');
ProductController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index(Request $request)
{
return view('welcome');
}
public function productFilter(Request $request)
{
$productName = null;
if (isset($request->productName)) {
$productName = $request->productName;
}
return response()->json($productName);
}
}
welcome.blade.php
<div>Product Name: <span id="product-name"></span></div>
<select id="product_filter" name="product_filter">
<option value="plastic product">plastic product</option>
</select>
<button id="submit-button" type="button">Send data</button>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script>
function display(productName){
productName = $('#product_filter').val(); // Here, I'm getting selected value of dropdown
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url: "{{route('dashboard-product-data')}}",
type: "GET",
data:{
'productName' : productName // in header request I'm getting value [productName: plastic product] *
},
success:function(data){
console.log(data);
document.querySelector('#product-name').innerHTML = data
},
error:function(e){
console.log(e,'error');
}
});
}
const submitButton = document.querySelector('#submit-button')
submitButton.addEventListener('click', () => display())
</script>
I've tried doing my research on my issue but have not been able to solve it. I'm attempting to Ajax POST on click. I've read the most popular issue is due to the csrf_token, but I believe I have that handled properly?
I keep getting this error:
POST http://example.com/refreshCalendar 500 (Internal Server Error)
Here is my code...
My meta tag for the csrf token at the top of my master.blade.php file
<meta name="token" content="{{ csrf_token() }}">
Route:
Route::post('/refreshCalendar', ['as' => 'refreshCalendar', 'uses' =>'Calendar#refreshCalendar']);
Js function
function refreshCalendar(obj){
var month = obj.data('month');
var year = obj.data('year');
history.pushState(null, null, '/month/'+month+'/year/'+year);
var data = {
"month":month,
"year":year,
_token:$('meta[name="csrf-token"]').attr('content')
};
$.ajax({
type: "POST",
url: '/refreshCalendar',
dataType: 'html',
async:true,
data: data,
success: function(data){
$('#calendarHolder').html(data);
},
error: function(){alert("There was an error retrieving information");return false;}
});
}
My Controller:
namespace App\Http\Controllers;
use DateTime;
use Illuminate\Http\Request;
class Calendar extends Controller
{
public function refreshCalendar(Request $request)
{
//Set data to $request
$data = $request->all();
return show($data['month'], $data['year'], true);
}
}
<meta name="token" content="{{ csrf_token() }}">
_token:$('meta[name="csrf-token"]').attr('content')
Your meta tag name is token, however you are looking for the meta tag named csrf-token.
If meta is present, you should look to your network for issues.
That was the culprit for me:
I'm to trying to store a sort order to each article within a help centre for my new site using Laravel 5 and having a bit of trouble getting it to work. I'm using jQuery UI's .sortable for arranging the elements on the page, and since there are going to be multiple sections throughout the site where areas are sortable, my jQuery script is built in a way for a 'one script for all' purposes. Hence the use of data-* attributes and route name references.
Here is the code I've got so far:
routes.php
Route::post('admin/help-centre/category/{category_id}/section/{section_id}/article/sort-order', 'AdminHelpCentreArticleController#sortOrder');
AdminHelpCentreArticleController.php
public function sortOrder($category_id, $section_id)
{
/* Return ------------------------------------- */
return [
'category_id' => $category_id,
'section_id' => $section_id
];
}
show.blade.php (Admin Article Listing)
<ul id="help-center-articles-sort" class="sortable">
#foreach ($helpCentreArticles as $helpCentreArticle)
<li class="sortable-element" data-sortable-element-id="{{ $helpCentreArticle->id }}">
{{ $helpCentreArticle->title }}
</li>
#endforeach
</ul>
Save Order
scripts.js (includes CSRF Token _token)
var csrfToken = $('meta[name="csrf-token"]').attr('content');
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
if (options.type.toLowerCase() === 'post')
{
options.data += options.data?'&':''; // add leading ampersand if `data` is non-empty
options.data += '_token=' + csrfToken; // add _token entry
}
});
$(document).ready(function() {
$('.sortable').sortable();
$('.sortable-save').on('click', function(e) {
e.preventDefault();
var route = $(this).attr('href'),
sortableID = $(this).attr('data-sortable-id');
var data = $('#' + sortableID + ' .sortable-element').map(function() {
return $(this).attr('data-sortable-element-id');
}).get();
$.ajax({
type: 'POST',
url: route,
dataType: 'json',
data: { id_array: data },
success: function(data) {
console.log(data);
}, error: function(data) {
console.log(data);
},
});
});
});
Everything so far is working in terms of the return response in the console, which is Object {category_id: "1", section_id: "1"}. But no matter what I try, I cannot seem to pass through the data map to the controller to use it.
I've tried a bunch of guesswork since I cannot find a single decent tutorial on AJAX in Laravel 5 anywhere, and I've tried things such as adding a $data parameter to the sortOrder() method, I've tried Input::all() and Request::all but it all returns errors (I'm guessing cause it's not an actual form?).
Once I've got the data to be passed through to the controller I'll be able to save the sort order to the database easily enough. But I can't quite get to that stage, any ideas?
EDIT
I should probably note that I do have a HelpCentreArticle model and a HelpCentreArticleRequest request too, here's some of the code from each file in case they are also needed:
HelpCentreArticle.php
class HelpCentreArticle extends Model {
protected $fillable = [
'category_id',
'section_id',
'title',
'content',
'excerpt',
'is_visible',
'sort_order',
'created_by',
'updated_by',
];
}
HelpCentreArticleRequest.php
class HelpCentreArticleRequest extends Request {
/* Authorization ------------------------------ */
public function authorize()
{
return true;
}
/* Validation rules --------------------------- */
public function rules()
{
$rules = [
'title' => 'required|min:3',
'content' => 'required|min:10',
];
return $rules;
}
}
I wasn't sure if I needed to add HelpCentreSectionRequest $request as the last parameter of the sortOrder() method, so I could use $request->all() but it just returns a 422 (Unprocessable Entity) in the console log.
So it appears that the correct way was to use Input::get('id_array'); instead of $_POST['id_array'];, which I tried, but when I originally tried this I wasn't including use Input; at the top of my controller, as I thought this was already accessible, but it wasn't.
Adding use Input;, and using Input::get(); is now working as expected.
Here is the updated code:
AdminHelpCentreArticleController.php
public function sortOrder($category_id, $section_id)
{
/* Query Select ------------------------------- */
$helpCentreCategory = HelpCentreCategory::findOrFail($category_id);
$helpCentreSection = HelpCentreSection::findOrFail($section_id);
/* Variables ---------------------------------- */
$id_array = Input::get('id_array');
$sort_order = 1;
/* Query Update ------------------------------- */
foreach($id_array as $id) {
$helpCentreArticle = HelpCentreArticle::where('id', $id)->first();
$helpCentreArticle->sort_order = $sort_order;
$helpCentreArticle->save();
$sort_order++;
}
/* Return ------------------------------------- */
return ['success' => true];
}
Then you can obviously access success for an if else statement in your jQuery to manipulate the page.
My implementation of UI sortable with Laravel
index.blade.php
...
#foreach($photos as $photo)
<tr data-sortable="{{ $photo->pivot->position }}" data-id="{{ $restaurant->id }}" data-photo-id="{{ $photo->pivot->photo_id }}">
<td>
<i class="fa fa-sort" aria-hidden="true"></i>
</td>
...
</tr>
#endforeach
<script type="text/javascript">
$("#sortable-ui tbody").sortable({
helper: fixHelper,
update: function(event, ui) {
$("#sortable-ui tbody tr").each(function(index){
console.log($(this).data('id')+', '+(index+1));
$.ajax({
url: '{{ route('owner.photo.update.position') }}',
type: 'POST',
data: 'restaurant_id='+$(this).data('id')+'&photo_id='+$(this).data('photo-id')+'&position='+(index+1)
})
.done(function (response) {
console.log(response);
})
.fail(function (jqXhr) {
console.log(jqXhr);
});
});
}
}).disableSelection();
</script>
scripts.js
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
AjaxController.php
public function updatePhotoPosition(Request $request)
{
$restaurant = $this->restaurantRepository->getById($request->get('restaurant_id'));
$photoId = $request->get('photo_id');
$photo = $restaurant
->photos()
->wherePivot('photo_id', $photoId)
->first();
$photo->pivot->position = $request->get('position');
$photo->pivot->save();
}