RESOLVED : see changes in repository, in vue component, controller, new RUle Recaptcha, and blade file : https://github.com/IllichOulianov/m1_serveur_projet
I want to include a recaptcha in a comment form, using this package https://github.com/DanSnow/vue-recaptcha/ . It is correctly displayed under the input fields, the recaptcha behaves correctly when clicked, but I always get a 422 response in the console after verifying recaptcha and submitting :
POST http://localhost:8000/comments 422 (Unprocessable Entity)
dispatchXhrRequest # app.js:285
xhrAdapter # app.js:119
dispatchRequest # app.js:724
Promise.then (async)
request # app.js:531
Axios.(anonymous function) # app.js:551
wrap # app.js:984
submit # app.js:1851
submit # app.js:36913
invokeWithErrorHandling # app.js:40775
invoker # app.js:41100
original._wrapper # app.js:46453
and this response in the devtools network tab :
{"message":"The given data was invalid.","errors":{"g-recaptcha-response": > ["The g-recaptcha-response field is required."]}}
I tried some things but none worked. I guess the trouble lies in the html element, the script fields and/or the validation rules. What do I need to add/modify ?
My codes, and repository if needed : https://github.com/IllichOulianov/m1_serveur_projet
CommentForm.vue :
<template>
<div>
<form #submit.prevent="submit">
<div class="form-group">
<input type="text" class="form-control" placeholder="Votre nom" name="name" id="name" v-model="fields.name" />
<div v-if="errors && errors.name" class="text-danger">{{ errors.name[0] }}</div>
</div>
<div class="form-group">
<input type="email" class="form-control" placeholder="Votre e-mail" name="email" id="email" v-model="fields.email" />
<div v-if="errors && errors.email" class="text-danger">{{ errors.email[0] }}</div>
</div>
<div class="form-group">
<textarea class="form-control" placeholder="Votre commentaire" id="content" name="content" rows="5" v-model="fields.content"></textarea>
<div v-if="errors && errors.content" class="text-danger">{{ errors.content[0] }}</div>
</div>
<div class="form-group">
<vue-recaptcha sitekey="XXXXX"></vue-recaptcha>
<div v-if="errors && errors.recaptcha" class="text-danger">{{ errors.recaptcha[0] }}</div>
</div>
<button type="submit" class="btn btn-primary">Publier le commentaire (après modération)</button>
</form>
</div>
</template>
<script>
import VueRecaptcha from 'vue-recaptcha';
export default {
components:{VueRecaptcha},
data() {
return {
fields: {
name:'',
email:'',
content:'',
article_id:window.article_id,
},
errors: {},
}
},
methods: {
submit() {
this.errors = {};
axios.post('/comments', this.fields).then(response => {
alert('Message sent!');
}).catch(error => {
if (error.response.status === 422) {
this.errors = error.response.data.errors || {};
}
});
},
},
}
</script>
COmmentCOntroller
public function store(Request $request,CommentsRepositoryInterface $interface) {
$this->validate($request, [
'name'=>'bail|required|min:3|alpha_num',
'email'=>'bail|required|email',
'content'=>'bail|required|between:3,5000',
'g-recaptcha-response'=>'required|recaptcha'
]);
$interface->save($request);
return response()->json(null, 200);
}
IN my blade file:
<script src="https://www.google.com/recaptcha/api.js?onload=vueRecaptchaApiLoaded&render=explicit" async defer>
</script>
<script type="text/javascript">article_id={{$article->id}};</script>
[...]
<div id="app">
<comment-form></comment-form>
</div>
<script src={{asset("js/app.js")}}></script>
Thanks !
Related
I've been trying to submit the form via a Fetch API, but having no luck so far. I can submit the form without one, but for this exercise it has to be with Fetch.
Form
<form action="{{ url('/process')}}" method="POST">
#csrf
<div class="form-container">
<div class="form-item">
<label for="name">Full Name<span class="required">*</span></label>
<input type="text" name="name" id="name" placeholder="Enter your name" />
</div>
<div class="form-item">
<label for="email">Email<span class="required">*</span></label>
<input type="email" name="email" id="email" placeholder="Enter your email address" required />
</div>
</div>
<div class="form-container">
<button type="submit">Submit</button>
</div>
</form>
^This submits successfully as is, but again I need to use Fetch.
Fetch API:
form.addEventListener("submit", (e) => {
e.preventDefault();
const csrfToken = document.querySelector("input[name='_token']").value;
fetch("success.blade.php", {
method: "post",
body: JSON.stringify(process),
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken,
},
})
.then((response) => {
console.log(response);
return response.text();
})
.then((text) => {
return console.log(text);
})
.catch((error) => console.error(error));
}
Routes
Route::get('/', [ContactController::class, 'create']);
Route::get('/all', [ContactController::class, 'getAll']);
Route::post('/process', [ContactController::class, 'store']);
ContactController.php
public function store(Request $request)
{
$contact = Contact::create($request->input());
$message = 'Thank you for your message! We will review your submission and respond to you in 24-48 hours.';
if ($request->ajax()) {
return response()->json(compact('message'));
}
return view('success');
}
success.blade.php is a file I created to display that thank you message, but something tells me I don't need it if I'm using this function store right.
If I remove action="{{ URL('/process') }} , and just use the Fetch API, then I get this error:
The POST method is not supported for this route. Supported methods: GET, HEAD.
you should not send fetch request to the blade
you must send request to controller
change url of fetch with controller
I use laravel 9, inertia and vue js on my production mode project. I have deploy my project to shared hosting.
But if i test to fill blank the form input, the validation is not working, and redirect me back to login page. My expecation is the validate message will show under the textbox, but its not.
My store function:
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|unique:kecamatans',
'order' => 'required',
]);
Kecamatan::create([
'name' => $request->name,
'order' => $request->order,
]);
return redirect()->route('apps.kecamatans.index');
}
My form:
<form #submit.prevent="submit">
<div class="mb-3">
<label class="fw-bold">Nama Kecamatan</label>
<input class="form-control" v-model="form.name" :class="{ 'is-invalid': errors.name }" type="text"
placeholder="Nama kecamatan">
</div>
<div v-if="errors.name" class="alert alert-danger">
{{ errors.name }}
</div>
<div class="mb-3">
<label class="fw-bold">Urutan</label>
<input class="form-control" v-model="form.order" :class="{ 'is-invalid': errors.order }" type="text"
placeholder="Urutan">
</div>
<div v-if="errors.order" class="alert alert-danger">
{{ errors.order }}
</div>
<div class="row">
<div class="col-12">
<ButtonInCreateVue url="kecamatans"></ButtonInCreateVue>
</div>
</div>
</form>
My setup script:
setup() {
const form = reactive({
name: '',
order: '',
latitude: '',
longitude: ''
});
const submit = () => {
Inertia.post('/apps/kecamatans', {
name: form.name,
order: form.order,
}, {
onSuccess: () => {
successAlert('Kecamatan', 'ditambah')
},
});
}
return {
form,
submit,
}
}
My route:
Route::resource('/kecamatans', \App\Http\Controllers\Apps\KecamatanController::class, ['as' => 'apps'])
->middleware('permission:kecamatans.index|kecamatans.create|kecamatans.edit|kecamatans.delete');
Its working normally on my local computer, but not working on hosting. And if i check console log, there is no error.
Have you passed your authentication token in ajax request?
Try passing authonetication token in your headers, then it should work.
This is my blade view where i am taking values of total closing report and total deposit. if it differs, automatically difference is being calculated and displayed. On the basis of difference, below details need to be added for the difference amount and stored in cashoutexpense.
For example - Total Closing Report is 200 , Total Deposit is 100 then details need to be provided for difference amount. i.e 100. In this case, suppose user has given input for expense amount as 50 in one textbox and 50 in another by adding another row.
<div class="form-group row">
<div class="col-12">
<label>Total Closing Report</label>
<input class="form-control" v-model="form.closing_report_total" #change="calculate" type="number" required name="closing_report_total" placeholder="Enter Closing Report Total">
</div>
</div>
<div class="form-group row">
<div class="col-12">
<label>Total Deposit</label>
<input class="form-control" type="number" v-model="form.total_deposit" #change="calculate" required name="total_deposit" placeholder="Enter Total Deposit">
</div>
</div>
<div class="form-group row">
<div class="col-12">
<label>Difference</label>
<input class="form-control" type="number" v-model="form.difference" name="difference" placeholder="Enter Difference" readonly>
</div>
</div>
<div class="form-group row" v-if="form.difference != 0" v-for="(comment, k) in form.cashoutexpenses" :key="k">
<div class="col-4">
<input type="text" class="flatpickr form-control" v-model="comment.expense_date" required name="expense_date" placeholder="Click here to choose Date">
</div>
<div class="col-4">
<input class="form-control" type="number" v-model="comment.amount" required name="amount" placeholder="Enter Expense Amount" #change="calculate_error">
</div>
<div class="col-4">
<input class="form-control" type="text" v-model="comment.comment" required name="comment" placeholder="Enter Expense Comments">
</div>
<div class="col-4">
<button class="btn btn-sm btn-danger" type="button" #click="removeRow(k)">Remove</button>
</div>
</div>
<div class="form-group row" v-if="form.left > 0">
<div class="col-12">
<input class="btn btn-primary form-control" type="button" value="Add Row" #click="addRow()">
</div>
</div>
Below is my script code for the same.
<script>
var app = new Vue({
el: '#app',
mounted: function() {
},
computed: {
difference: function() {
return this.form.difference = this.form.closing_report_total - this.form.total_deposit;
}
},
data: {
form: {
closing_report_total : 0.00,
total_deposit: 0.00,
difference: 0.00,
left: 0.00,
comments: '',
cashoutexpenses: [],
buffer: []
},
},
methods: {
addRow() {
this.form.cashoutexpenses.push({
date: '',
amount: '',
comment: '',
});
this.form.buffer = JSON.parse(JSON.stringify(this.form.cashoutexpenses));
},
removeRow(index) {
this.form.cashoutexpenses.splice(index, 1);
this.form.buffer = JSON.parse(JSON.stringify(this.form.cashoutexpenses));
},
calculate: function() {
this.form.difference = this.form.closing_report_total - this.form.total_deposit;
this.form.cashoutexpenses = [];
this.form.buffer = JSON.parse(JSON.stringify(this.form.cashoutexpenses));
if(this.form.difference != 0) {
this.addRow();
}
},
calculate_error: function() {
this.form.left = this.form.difference - this.form.cashoutexpenses.reduce((a,b)=> (a + (parseInt(b['amount'])||0)),0);
if(this.form.left<0) {
alert("Sum of expenses should not exceed total amount");
for(var i=0; i<this.form.cashoutexpenses.length;i++) {
if(this.form.cashoutexpenses[i].amount!=this.form.buffer[i].amount) {
this.form.buffer[i].amount = 0;
this.form.cashoutexpenses[i].amount=this.form.difference - this.form.buffer.reduce((a,b)=> (a + (parseInt(b['amount'])||0)),0);
}
}
}
else if(this.form.left>0) {
alert("Please add "+this.form.left+" more");
}
this.form.buffer = JSON.parse(JSON.stringify(this.form.cashoutexpenses));
},
formSubmit: function(e) {
e.preventDefault();
let currentObj = this;
// let data = new FormData();
let formData = new FormData()
formData.append('closing_report_total', this.form.closing_report_total);
formData.append('total_deposit', this.form.total_deposit);
formData.append('difference', this.form.difference);
formData.append('form.cashoutexpenses', this.form.cashoutexpenses);
let config = { headers: { 'Content-Type': 'multipart/form-data' } }
axios.post('/cashoutdetails/store', formData ,config)
.then(response => {
//console.log(formData);
//alert('data saved');
//window.location.href = "{{ route('cashoutdetails.index')}}";
})
.catch(function (error) {
alert('Error');
});
}
}
})
</script>
if i check for the same in vue plugin of mozilla firefox, correct data is being displayed which is as follows --
cashoutexpenses:Array[2]
0:Object
amount:"50"
comment:"test2"
date:""
expense_date:"2021-10-10"
1:Object
amount:"50"
comment:"test3"
date:""
expense_date:"2021-10-12"
Now in my controller , i am fetching the data which is as below -
$comments = $request->form_cashoutexpenses;
//return $comments;
foreach($comments as $c) {
return $c;
$cashout_comments = new CashOutExpenses;
$cashout_comments->cashout_id = $cashout_details->id;
$cashout_comments->expense_date = $c['expense_date'];
$cashout_comments->amount = $c['amount'];
$cashout_comments->explanation = $c['comment'];
$cashout_comments->save();
}
return response()->json([
'message' => 'Details added!',
], 201);
If i return $comments, it gives reply as [object Object],[object Object] otherwise it returns error for foreach.
if i return $request->all(); ... it gives following output -
{"closing_report_total":"200","total_deposit":"100","difference":"100","form.cashoutexpenses":"[object Object]"}
Please help me to save this data in cashout_expense table.
I do not fully understand your code, but I've noticed some potentially problematic parts:
$request->form_cashoutexpenses most likely is not an array, that is why you probably getting this error. You can check it putting dd($comments, gettype($comments)); after declaration of $comments and rerunning page
Having return statement inside foreach loop at the first line seems to be silly, because the code below will never be executed.
Join.vue
<template>
<div class="container join-form">
<form>
<div class="container">
<h2>Join to session</h2>
</div>
<div class="form-group">
<label for="sessionId">Session ID:</label>
<input v-model="sessionId" class="form-control" type="text" name="sessionId" placeholder="Session ID">
</div>
<div class="form-group">
<label for="userName">Username:</label>
<input v-model="userName" class="form-control" type="text" name="userName" placeholder="Your name">
</div>
<button v-on:click.prevent="joinSession()" class="btn btn-primary">Join session</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
userName: "",
sessionId: this.$route.params.sessionId,
userId: null
};
},
methods: {
joinSession() {
this.$http
.post('/joinsession', {
userName: this.userName,
sessionId: this.sessionId
})
.then(
data => {
this.userId = data.body.userId;
this.$router.push("/user/" + this.sessionId + "/" + this.userId);
},
() => {
this.$router.push("/error");
}
);
}
}
};
</script>
<style>
.join-form {
width: 50%;
}
</style>
I wanna have "/user/{sessionId}/{userId}" but instead I've got
"http://projectx.laragon:8090/user/3/[object%20Object],[object%20Object]"
How can I solve this issue? I am using Laravel in combination with Vue-Resource &% Vue-Router in order to switch between different .vue files that are all together in an App.vue. This means my URL is not actively entered, it is just displayed without fetching from the server.
eg: "user/{userId}/{sessionId}" is a 404 if I didn't register it in my web.php
It seems you actually want do
this.$router.push( { path: '/user', params: { sessionId: this.sessionId, userId: this.userId } });
If it still not work, check your route configuration and sessionId / userId values.
For more info, see the docs
I'm trying to submit data to the database via ajax. The submit article page works fine without ajax. I've added console.log() just to see if anything is going through, but instead I'm getting this error:
POST http://localhost/laravel-5/public/articles/create 500 (Internal Server Error)
What's wrong with my code? Is it the javascript or the controller?
EDIT: I'm getting this in laravel.log
exception 'Illuminate\Session\TokenMismatchException' in C:\xampp\htdocs\laravel-5\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken.php:53
Route
Route::resource('articles', 'ArticlesController');
Controller
public function store(Requests\ArticleRequest $request)
{
$article = new Article($request->all());
Auth::user()->articles()->save($article);
$response = array(
'status' => 'success',
'msg' => 'Article has been posted.',
);
return \Response::json($response);
}
jQuery
$(document).ready(function() {
$('#frm').on('submit', function (e) {
e.preventDefault();
var title = $('#title').val();
var body = $('#body').val();
var published_at = $('#published_at').val();
$.ajax({
type: "POST",
url: 'http://localhost/laravel-5/public/articles/create',
dataType: 'JSON',
data: {title: title, body: body, published_at: published_at},
success: function( data ) {
$("#ajaxResponse").append(data.msg);
console.log(data);
}
});
});
View
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<h1>Write a New Article</h1>
<hr>
{!! Form::open(['url' => 'articles', 'id' => 'frm']) !!}
<p>
{!! Form::label('title', 'Title:') !!}
{!! Form::text('title') !!}
</p>
<p>
{!! Form::label('body', 'Body:') !!}
{!! Form::textarea('body') !!}
</p>
<p>
{!! Form::label('published_at', 'Date:') !!}
{!! Form::input('date', 'published_at', date('Y-m-d'), ['class' => 'form-control']) !!}
</p>
<p>
{!! Form::submit('Submit Article', ['id' => 'submit']) !!}
</p>
{!! Form::close() !!}
<h3 id="ajaxResponse"></h3>
#if($errors->any())
<ul>
#foreach($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
#endif
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="{{ URL::asset('assets/js/ArticleCreate.js') }}"></script>
});
When you make a request via POST to a resource controller, it automatically calls store method:
Verb Path Action Route Name
----------------------------------
POST /articles store articles.store
So, you just need to change ajax url to:
$.ajax({
type: "POST",
url: 'http://localhost/laravel-5/public/articles',
When you need to send the session token, you can add a global meta-tag like this is you website:
<meta name="csrf-token" content="{{ csrf_token() }}">
Then, just add the token via ajax's headers:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
If you are using Form::open() function (LaravelCollective) it adds a hidden input with the token as value with the name _token. So, you can remove the meta-tag and edit your ajax's headers like this:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('[name="_token"]').val()
}
});
That's what I got exception 'Illuminate\Session\TokenMismatchException' in C:\xampp\htdocs\laravel-5\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken.php:53
You're hitting Laravel's CSRF protection.
http://laravel.com/docs/5.1/routing#csrf-protection
You need to pass the hidden _token field's value. This can be done automatically on all jQuery-initiated AJAX requests by doing this in your application's JS:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('input[name="_token"]').value()
}
});
Or, you can manually fetch and pass the value of the _token hidden field in each of your AJAX calls.
I like to share this code to help someone to need ajax post and get with laravel
<<<<<<<<
POST
<<<<
<<look after #extends<<
<<look beforeSend: function (xhr) <<
<<look use Illuminate\Http\Request in Routes<<
<<<------<<views\login\login.blade.php<<<<-----------<<<
#extends('cuerpito.web')
<meta name="csrf_token" content="{{ csrf_token() }}" />
#section('content')
<form action="#" id="logForm" method="post" class="form-horizontal">
<div class="form-group">
<div class="col-xs-12">
<div class="input-group">
<input type="email" id="email" name="email" class="form-control input-lg" placeholder="Ingresa tu Email." autocomplete="off">
</div>
</div>
</div>
<div class="form-group">
<div class="col-xs-12">
<div class="input-group">
<input type="password" id="password" name="password" class="form-control input-lg" placeholder="Ingresa tu Contraseña." autocomplete="off">
</div>
</div>
</div>
<div class="form-group formSubmit">
<div class="col-xs-12">
<div class="input-group">
<button type="submit" name="feedbackSubmit" id="feedbackSubmit" class="btn btn-success btn-lg" style="display: block; margin-top: 10px;">Ingresar</button>
</div>
</div>
</div>
</form>
<script type="text/javascript">
$(document).ready(function () {
$("#feedbackSubmit").click(function () {
$.ajax({
url: '{{URL::route('login4')}}',
type: "post",
beforeSend: function (xhr) {
var token = $('meta[name="csrf_token"]').attr('content');
if (token) {
return xhr.setRequestHeader('X-CSRF-TOKEN', token);
}
},
data: $("#logForm").serialize(),
success: function (data)
{
if (data) {
alert(data);
console.log(data);
} else {
console.log(data);
}//else
}//success
});//ajax
return false;
});//feedbacksubmit
});//document ready
</script>
-------------0----------------------
-------------0----------------------
<<<----<<app\Http\routes.php<<<<-----------<<<
<?php
use Illuminate\Http\Request;
Route::post('login4', function()
{
return 'Success! POST Ajax in laravel 5';
})->name('login4');
------------------0----------------------
------------------0----------------------
<<<<
Get
<<look after #extends<<
<<look beforeSend: function (xhr) <<
<<look use Illuminate\Http\Request in Routes<<
<<<------<<views\login\login.blade.php<<<<-----------<<<
#extends('cuerpito.web')
<meta name="csrf_token" content="{{ csrf_token() }}" />
#section('content')
<form action="#" id="logForm" method="post" class="form-horizontal">
<div class="form-group">
<div class="col-xs-12">
<div class="input-group">
<input type="email" id="email" name="email" class="form-control input-lg" placeholder="Ingresa tu Email." autocomplete="off">
</div>
</div>
</div>
<div class="form-group">
<div class="col-xs-12">
<div class="input-group">
<input type="password" id="password" name="password" class="form-control input-lg" placeholder="Ingresa tu Contraseña." autocomplete="off">
</div>
</div>
</div>
<div class="form-group formSubmit">
<div class="col-xs-12">
<div class="input-group">
<button type="submit" name="feedbackSubmit" id="feedbackSubmit" class="btn btn-success btn-lg" style="display: block; margin-top: 10px;">Ingresar</button>
</div>
</div>
</div>
</form>
<script type="text/javascript">
$(document).ready(function () {
$("#feedbackSubmit").click(function () {
$.ajax({
url: '{{URL::route('login2')}}',
type: "get",
beforeSend: function (xhr) {
var token = $('meta[name="csrf_token"]').attr('content');
if (token) {
return xhr.setRequestHeader('X-CSRF-TOKEN', token);
}
},
data: $("#logForm").serialize(),
success: function (data)
{
if (data) {
obj = JSON.stringify(data, null, " ");
var datito = JSON.parse(obj);
console.log(datito.response);
alert(datito.response);
} else {
console.log(data);
}//else
}//success
});//ajax
return false;
});//feedbacksubmit
});//document ready
</script>
-------------0----------------------
-------------0----------------------
<<<----<<app\Http\routes.php<<<<-----------<<<
<?php
use Illuminate\Http\Request;
Route::get('login2', 'WebController#login2')->name('login2');
-------------0----------------------
-------------0----------------------
<<<----<<Http\Controllers\WebController.php<<<<-----------<<<
public function login2(Request $datos) {
if ($datos->isMethod('get')) {
return response()->json(['response' => 'This is get method']);
}
return response()->json(['response' => 'This is post method']);
}
-------------0----------------------
-------------0----------------------
You can add your URLs to the VerifyCsrfToken.php middleware. The URLs will be excluded from CSRF verification:
protected $except = [ "your url", "your url/abc" ];
Well if you are looking for the sure shot answer ,here it is :
This error particularly occurs if you are missing csrf_token() in your code
Here is what I did,
<h6>ITEMS ORDERED:CLICK HERE</h6>
<input type="hidden" id="token" value="{{ csrf_token() }}">
Now With Ajax
<script type="text/javascript">
function getcart(val) {
var alpha=val;
var token=document.getElementById('token').value;
$.ajax({
type:'post',
url:'/private/getcart',
data:{'alpha':alpha,'_token': token},//this _token should be as it is
success:function (result) {
alert(result);
}
});
}
</script>
In my laravel controller
public function getcart(Request $req)
{
return response ("its");
}