How to catch validation failure with laravel when using form requests - php

I'm using a formrequest file with laravel 5.2 to check input. This form i'm calling using jquery's $.post function. In my console, it's return 422 Unprocessable Entity which I suspect to be coming from the response since i'm not formatting it to json. One way of doing is this
I'd would like to know how I can invoke from my form request, the means to change the output messages to json.
Thanks!
UPDATE 1
JQuery looks like this:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('input[name="csrf-token"]').val()
}
});
$("#changePassword").on('click', function(e){
e.preventDefault();
var data = {};
data.name = $("input[name='name']").val();
data.surname = $("input[name='surname']").val();
data._token = $('input[name="_token"]').val();
$.post('url',data).done(function(data){
$(".message").empty().html("Done!");
}).fail(
function(response, status){
}
);
});

Override the response method in your request class like below
/**
* Get the proper failed validation response for the request.
*
* #param array $errors
* #return \Symfony\Component\HttpFoundation\Response
*/
public function response(array $errors)
{
return Response::json([
'error' => [
'message' => $errors,
]
]);
}

Related

Ajax GET request is empty despite correct query string parameter

Using a simple Ajax GET request to retrieve some data, it successfully checks if($request->ajax()) {} but then fails any validation because there is no data in the Request $request variable. This happens only on the production server, on localhost everything works fine.
The console shows the intended URL https://example.com/employeeInfo?id=1, then error 422 (Unprocessable Entity). Output from error: function(jqxhr, status, exception) { alert('Exception:', exception); } gives an empty alert message.
View
<script>
(function ($) {
$(document).ready(function() {
$(".team-pic").off("click").on("click", function() {
$id = $(this).data('id');
// Get data
$.ajax({
type: 'GET',
url: 'employeeInfo',
data: {'id':$id},
success: function(data){
var obj=$.parseJSON(data);
// Show output...
},
error: function(jqxhr, status, exception) {
alert('Exception:', exception);
}
});
});
});
}(jQuery));
</script>
Route
Route::get('/employeeInfo', 'EmployeeController#get');
Controller
public function get(Request $request) {
if($request->ajax()) {
$this->validate($request, [
'id' => 'required|integer',
]);
// Id
$employee = Employee::find(request('id'));
// Create output
$data = ...
echo json_encode($data);
}
}
If I were you, I would use a RESTful API with route model binding, specifically the explicit binding.
RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::model('employee', App\Employee::class);
}
Route
Route::get('api/employees/{employee}', 'EmployeeController#get');
Controller
public function get(Employee $employee)
{
// The id being valid is already done by forcing it to be an Employee
// It is also an ajax call because it is going to the api route
// This will json_encode the employee object.
return $employee;
}

Getting TokenMismatchException when using nested AJAX calls. Laravel 5.4

I get TokenMismatchException when using nested AJAX calls. The first AJAX call works fine but the second always goes to error instead of success.
What I'm trying to do is that when the user registers from the button in the nav bar I want him to go to the dashboard or /home - this works okay. But, when the user fills the form (to buy something) on the index page, I want him to:
Have his input checked for validity, then, check if he's logged in, if not then the registration modal pops up. After he's registered I want him to be redirected to the checkout page.
However, what happens is that when the user fills the buying form and hits submit, the first ajax checks if the input in the buying form is valid, if it is, then check if he's logged in if not return 401 error.
401 gets picked up by the first ajax and directs the flow to 401 handling where the registration modal pops up to register, that's when the 2nd ajax pop up. After he's registered the back-end keeps returning 500 because of CSRF token mismatch.
First, this is the nested ajax:
<script type="text/javascript">
$(document).ready(function(){
$('#topup-form').submit(function(e){
e.preventDefault();
var topup_info = $('form').serialize();
//FIRST AJAX
$.ajax({
url: $('form').attr('action'),
method: 'post',
data: topup_info,
type: 'json',
//if success show success message for user
success: function(result){
alert(result.responseJSON.code);
$('.alert.error').slideUp(200);
$('.alert.success').append("<p class='lead'>Thanks! To checkout we go!</p>").slideDown(200);
},
//for error check if it's 400 (validation) or 401(authentication)
error: function(errorData){
// alert(errorData.responseJSON.code);
if(errorData.responseJSON.code === 400){
var error = errorData.responseJSON.message;
$('.alert.error').text('');
$('.alert.success').slideUp(200);
for (var i in error){
for (var j in error[i]) {
var message = error[i][j];
$('.alert.error').append("<p class='lead'>" + message + "<p>");
}
}
$('.alert.error').slideDown(00);
}//end error 400
//for authentication failure, show registeration modal
else if (errorData.responseJSON.code === 401) {
//change somethings in registeration modal
$('#myModalLabel').html('Please Login First');
$('#register').trigger('click');
document.getElementById('formRegister').action = "{{ route('user.regtopup') }}";
//when registeration form is submitted..
$('#formRegister').submit(function(e){
e.preventDefault();
//fire 2nd ajax
$.ajax({
url: $('#formRegister').attr('action'),
method: 'post',
data: $('form').serialize(),
type: 'json',
success: function(result){
alert('success!!!');
},
//it keeps going to error! complaining about csrf token mismatch
error: function(result){
console.log(JSON.stringify(result));
},
})//end of 2nd ajax
});//end of 2nd submit
}//end of 401
}//end of error
});//end of first ajax
});//end of first submit
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
}
})
});
</script>
Second, this is the controller that checks input validity and return 401 when not registered:
public function etiPost(Request $request) {
$validator = [
'topupAmount'=> 'required|integer|between:10,500',
'phonenumber'=> 'required|regex:/^05[602][0-9]{7}$/',
];
$inputs = $request->all();
Log::info($inputs);
$validator = Validator::make($inputs, $validator);
if($validator->fails()){
return Response::json([
'error' => true,
'message' => $validator->messages(),
'code' => 400
], 400);
}
elseif (Auth::check()) {
return view('pages.checkout', compact('inputs'));
}
else {
return Response::json([
'error' => true,
'message' => "Please login first",
'code' => 401
], 401);
}
}
This is the overloaded register method that returns JSON when registration is successful. Here is where 500 is returned! When I Log the returned JSON it comes out as normal 200 response but it arrives at the "Whoops" 500 error to the 2nd ajax! The user is registered successfully in the database but this method returns 500 which is caught by the error part of the ajax call.
/**
* Handle a registration request for the application (overloaded).
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function register(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
$this->guard()->login($this->create($request->all()));
// return response()->json();
return response()->json(['msg' => 'Success! You have been registered!'], 200);
}
I won't include the forms for brevity but rest assured I added all the CSRF input tags and the meta tag in the head of the HTML.
What should I do differently to avoid this? The first ajax works but the second doesn't.
Set header token for each ajax call
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
Also note that you have to add mete token in your template
you can add meta token like this in template
<meta name="csrf-token" content="{{ csrf_token() }}">
if you still want to disable csrf token then
Excluding URIs From CSRF Protection
Sometimes you may wish to exclude a set of URIs from CSRF protection. For example, if you are using Stripe to process payments and are utilizing their webhook system, you will need to exclude your Stripe webhook handler route from CSRF protection since Stripe will not know what CSRF token to send to your routes.
Typically, you should place these kinds of routes outside of the web middleware group that the RouteServiceProvider applies to all routes in the routes/web.php file. However, you may also exclude the routes by adding their URIs to the $except property of the VerifyCsrfToken middleware:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
];
}
Instead of 'stripe/*', if you give '/*' then it will disable token for all
For more detail :
https://laravel.com/docs/5.5/csrf#csrf-x-csrf-token
please follow code in your jquery before ajax call
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
it's not advisable to add CSRF token in meta because all pages are not contain form to submit value so use this solution so you can use CSRF only for your js perspective.
Thank you

Request conflict symfony action

I have an Ajax call that calls an action in the controller.
The Ajax call looks like this
$(document).on('click', '.editQuestionButton', function() {
var question_id = $(this).data('question');
console.log(question_id);
$.ajax({
type: "POST",
url: "/dashboard/form/AjaxEditQuestionForm/" + question_id + "",
success: function(data) {
$('#form-modal').html(data);
}
});
});
$(document).on('click', '.modal .fn-submit', function() {
$(this).closest('.modal').find('form').submit();
});
And this is the action.
/**
* #Route("/AjaxEditQuestionForm/{question}")
* #Template
* #ParamConverter("question", class="AppBundle:Question")
*/
public function ajaxEditQuestionFormAction(Request $request, $question)
{
$edit_question_form = $this->createForm(new AddQuestionType(), $question);
$edit_question_form->handleRequest($request);
if ($edit_question_form->isValid()) {
$em->flush();
return $this->redirectToRoute('app_form_create');
}
else{
die();
}
return array(
'question' => $question,
'editAjaxQuestionForm' => $edit_question_form->createView(),
);
}
The problem is that the action never returns the form but goes straight into checking if the form is valid.
I figure this has something to do with the $request but I'm not sure how to change this.
The action should first get the data from the Ajax call, return the form and if the form is submitted, check if the form is valid and flush the Question entity.
Any idea on how I should do this?

dropzone not uploading, 400 bad request, token_not_provided

okay i've been trying this for like 2 hrs now and cant to make this work. dropzone cant upload any file. the server says "token not provided". im using laravel as backend and it uses jwt tokens for authentication and angular as front end. here's my dropzone config.
$scope.dropzoneConfig = {
options: { // passed into the Dropzone constructor
url: 'http://localhost:8000/api/attachments'
paramName: 'file'
},
eventHandlers: {
sending: function (file, xhr, formData) {
formData.append('token', TokenHandler.getToken());
console.log('sending');
},
success: function (file, response) {
console.log(response);
},
error: function(response) {
console.log(response);
}
}
};
and the route definition
Route::group(array('prefix' => 'api', 'middleware' => 'jwt.auth'), function() {
Route::resource('attachments', 'AttachmentController', ['only' => 'store']);
}));
and the controller method
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$file = Input::file('file');
return 'okay'; // just until it works
}
the token is correct and is actually getting to the server (because i tried returning the token using Input::get('token') in another controller function and it works). can someone tell me what im doing wrong? im getting "400 Bad Request" with "token_not_provided" message...
thanks for any help. and i apologize for my bad english..
I'm not sure why appending the token to the form isn't working, but you could try sending it in the authorization header instead.
Replace
formData.append('token', TokenHandler.getToken());
With
xhr.setRequestHeader('Authorization', 'Bearer: ' + TokenHandler.getToken());
make sure you add the token to your call: Example you can add the toke as a parameter in your dropzone url parameter.
//If you are using satellizer you can you this
var token = $auth.getToken();// remember to inject $auth
$scope.dropzoneConfig = {
options: { // passed into the Dropzone constructor
url: 'http://localhost:8000/api/attachments?token=token'
paramName: 'file'
},
eventHandlers: {
sending: function (file, xhr, formData) {
formData.append('token', TokenHandler.getToken());
console.log('sending');
},
success: function (file, response) {
console.log(response);
},
error: function(response) {
console.log(response);
}
}
};

Sending jQuery.serialize form via POST, expecting JSON response from controller

I would like to ask you how could I send ajax request with serialized by jQuery form and recieve JSON response from controller? I have been trying many solutions but none of them worked for me. I have a little experience in that matter.
Can you provide any good example? Thank You!
Send post with serialized form (POST) by AJAX
Process action in controller function and obtain JSON response in ajax -> success
I'm using CakePHP 2.4.1
My ajax request
$.ajax({
type: "post",
url: location.pathname + "/edit",
data: data,
success: function(response) {
$("#content").html(response); // i would like to recieve JSON response
alert(response); // here ;C
},
error: function(){
alert("error");
}
});
Part of my function in controller
public function admin_edit(){
//................ some logic passed
if($this->request->is('ajax')){
$this->layout = 'ajax';
$this->autoRender = false;
$this->set(compact('user'));
$this->disableCache();
foreach($this->request->data['User'] as $key => $value){
if(empty($value)){
unset($this->request->data['User'][$key]);
}
}
$this->User->id = $this->request->data['User']['id'];
if($this->User->save($this->request->data)){
$this->Session->setFlash('Użytkownik został zmodyfikowany');
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash('Nie zmodyfikowano użytkownika');
}
}
What i would like to recieve is JSON response from the controller.
example
[{"id":"1", "username":"test", ... }]
Ok, I think what confuse you are little stuff, but mixed together can be a bit hard to debug for someone with little experience. I'll post a basic example of what should work for you, and you iterate over that. Tell us if there's another error (it's easier to check for specific errors rather than the view/controller possible error).
First, in the ajax call, change for console.log(response); to debug better
//alert(response);
console.log(response);
},
error: function(){
alert("error");
console.log(response);
}
});
And in the controller
public function admin_edit(){
//................ some logic passed
if($this->request->is('ajax')){
/* layout not necessary if you have autoRender = false */
//$this->layout = 'ajax';
/* actually, no need for this either with _serialize, but add it if you have unwanted html rendering problems */
//$this->autoRender = false;
$this->set(compact('user'));
/* other code that doesn't really matter for the example ... */
$this->User->id = $this->request->data['User']['id'];
if($this->User->save($this->request->data)){
/* NO!! */
//$this->Session->setFlash('Użytkownik został zmodyfikowany');
//return $this->redirect(array('action' => 'index'));
$status = 'OK';
$message = 'Użytkownik został zmodyfikowany';
$this->set(compact('message', 'status'));
}
/* NO EITHER!! */
//$this->Session->setFlash('Nie zmodyfikowano użytkownika');
$status = 'NOT-OK';
$message = 'Not sure what your flash says but let\'s assume it an error alert';
$this->set(compact('message', 'status'));
//serialize variables you have set for the "ghost" view
$this->set('_serialize', array('user', 'message', 'status'));
}
}
I think your major flaw here is to return a redirect. That's a no no for json. What you are doing with that is giving the ajax call the html for the index action. That makes no sense. If you want the action to return JSON, then all cases must return json, no html whatsoever. So, no setFlash, no redirect. What I normally do here is to return the JSON data with a status and a message (like in the code above). Then, in the ajax call, on success, you parse the JSON data, read the status, if ok you redirect (via js), and if not, show the error message you got.
Hope it clear things for you.
[tiny edit]: json_encode will also work, but when in CakePHP, do what CakePHP(ians?) do (serialize) (because you don't need to echo the variables).
There's an example at JQuery.com
Example: Post to the test.php page and get content which has been returned in json format
<?php echo json_encode(array("name"=>"John","time"=>"2pm")); ?>
.
$.post( location.pathname + "/edit", data, function( data ) {
console.log( data.name ); // John
console.log( data.time ); // 2pm
}, "json");
So plugging in your ajax call is something like:
$.post( "test.php", data, function(response) {
$("#content").html(response);
alert(response);
}, "json");
Edit: If you're not getting the correct response, please show the php code that echos or returns the json..it's not anywhere in that function you provided.

Categories