Laravel normal login with vuejs using axios returns error Request failed with status code 422 - laravel

Am trying to use laravel and vue js with axios
Previously this was my login form
<form name="login-form" method="POST" action="{{ route('login') }}">
//username and password fields
</form>
Which works perfectly one can login
Now i would like to use vuejs in a similar way without making my app a single page app
so i have resulted to making a login component
<template>
<form name="login-form">
<input type="email" v-model="email" class="form-control" autofocus>
<input id="password" type="password" v-model="password" required>
<button type="submit"
class="btn btn-dark btn-sm"
#click.prevent="login"
:disabled="disableform">
{{submitted?"Logging you in .....":"Login"}}
</button>
</form>
</template>
Now script part
<script>
export default {
data: () => ({
email :'', password:'', remeberme:true, submitted:false
}),
methods: {
login() {
//do other stuff like disabling buttons...etc
axios.post("/login", {email:this.email, password:this.password})
.then(
(res)=>{
///showing sweet alert then
// settimout for 3 sec the
window.location.href="/dashboard";
},
(err)=>{
//show sweet alert of failed login
}
)
},
}
Now whenever i make the login post request an getting an error
Request failed with status code 422
I would like to proceed with the laravel vuejs workflow(with authentication sessions) but not an api jwt token request format.
What could be wrong or is this possible?

It's a validation error. check you laravel controller regarding post/put function.
Request failed with status code 422
When laravel fail the validation then it appears.

You need to use a csrf_field in your form, like this:
<input type="hidden" name="_token" :value="csrfToken">
And define it in your script:
data() {
return {
csrfToken: ''
}
},
created() {
this.csrfToken = document.querySelector('meta[name="csrf-token"]').content;
}

Related

Vue Laravel submit form using button in other component

I have two components:
Header.vue:
<button type="submit" #click="clickNext">Next</button>
methods:{
clickNext(){
Bus.$emit('submitForm');
}
}
And Home.vue
<form method="POST" ref="my-form">
<input type="text" name="email" id="email" />
</form>
created() {
Bus.$on('submitForm', () => this.$refs['my-form'].submit(this.send()))
},
methods: {
send() {
console.log("send!");
}
}
I need send form (with component Home.vue) using button which is in Header.vue component.
When I click Next button Laravel REFRESH PAGE and return error:
Symfony \ Component \ HttpKernel \ Exception \
MethodNotAllowedHttpException
No message
You have to have CSRF token in every form that does any method except GET.
Read on it in official docs

Form is not displaying when using csrf token

When the onclick function in Header.vue is clicked I'm getting this error but when I delete the input tag with csrf_token from the form in Register.vue, then the register form is showing as it is supposed to.
Although after submitting the inputs by POST I'm left with the standard 419 (Sorry, your session has expired. Please refresh and try again.) Laravel screen.
I'm sure the 419 screen is caused by lack of CSRF token, so my final question is how do I implement it in vue.js?
I'm using Vue.js and Laravel to create a SPA, in my Register.vue component which renders onclick on top of the site I've added CSRF token as follows:
<template>
<form id="registerForm" class="register-container" action="registerUser" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="register-container__form">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" name="email" type="text">
<label class="mdl-textfield__label">Email</label>
</div>
.
.
.
</template>
The onclick function which pops up the registration form is in Header.vue:**
<template>
.
.
.
<nav class="mdl-navigation">
<a class="mdl-navigation__link" href="" v-on:click.prevent="registerPopUp()">Sign In</a>
</nav>
<register-form/>
</template>
<script>
import Register from './Register.vue'
export default {
components: {
'register-form': Register
},
methods: {
registerPopUp: () => {
let loginForm = document.getElementById('loginForm');
let registerForm = document.getElementById('registerForm');
loginForm.style.display = "none";
registerForm.style.display = "block";
window.onclick = (e) => {
if(e.target == registerForm)
registerForm.style.display = "none";
}
}
}
}
</script>
Yeah you can't put blade directives in the vue template, this is why you're form isn't rendering and you're getting that error, you haven't actually selected a form and then you're trying to access a property on it.
If you are using axios to make your requests to the server from js, the default resources/js/bootstrap.js file will register the csrf token with axios, just make sure you still have the csrf token placed into a meta field on your layout like this:
<meta name="csrf-token" content="{{ csrf_token() }}">
If you aren't using axios, you can access the csrf token from that meta field within JS like this:
let token = document.head.querySelector('meta[name="csrf-token"]');
If you really need that hidden field there (maybe you're submitting the form with a regular html submit button and not js) you could put this in the "created()" section of the vue component:
this.csrf_token = document.head.querySelector('meta[name="csrf-token"]');
and then in your template:
<input type="hidden" name="_token" :value="csrf_token">

Laravel vue axios is action method and csrf needed for ajax forms

I am posting a ajax from in Laravel using axios and vue, I have a #click="postData" button in the form that toggles a axios post request:
postData() {
axios({
method: 'post',
url: appJS.base_url + '/comment',
responseType: 'json',
data: comData
})
.then(function(response) {
})
But do I still need to add the action, method and csrf to my form?
<form action="{{ url('/comment') }}" method="POST">
{{ csrf_field() }}
</form>
vs
<form></form>
Everything works fine just using <form></form> but I wonder if there are any pros/cons?
I am making a ajax call in the background since I dont want the whole page to reload
You definitely don't need action and method attributes on form tag, because they are already defined on your axios call.
As for the csrf_field(), you probably still need it, because Laravel has a preconfigured middleware called VerifyCsrfToken. But it depends if you use it or not.
you can using event form in vuejs, you need't using ajax, you can try the following code, but if laravel + vuejs, need add Enable CORS for a Single Route in laravel:https://gist.github.com/drewjoh/43ba206c1cde9ace35de154a5c84fc6d
export default{
data(){
return{
title:"Form Register",
}
},
methods:{
register(){
this.axios.post("http://localhost:8888/form-register",this.formdata).then((response) => {
console.log(response);
});
},
}
}
<form action="" method="post" v-on:submit.prevent="register">
<div class="panel-heading">{{title}}</div>
<div class="form-group">
<button type="submit" class="btn btn-danger">Register</button>
<button type="reset" class="btn btn-success">Reset</button>
</div>
</form>

POST form data using AJAX and return object

I have created a form which has two inputs and a submit button. The form will post to a RESTful service which is all set up.
I want to use AJAX in order to POST to this RESTful service and then return the object so I can then validate the form.
The object will return something like this for an error and one similar for success
{"status":"error","message":"Incorrect username or password."}
My code is below. When i test I am using XAMPP on localhost:81. When I submit the form I receive this error.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
<form name="login-form" id="login-form" method="post" action="SERVICEHERE">
<label id="error">Sorry it seems your credentials are incorrect</label>
<div class="inputBlock">
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</div>
<div class="inputBlock">
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</div>
<input type="submit" value="Login" id="submit" />
</form>
$(document).ready(function () {
$("#login-form").submit(function (e) {
e.preventDefault();
var formData = $("#login-form").serialize();
console.log(formData);
$.ajax({
type: "GET",
url: $("#login-form").attr('action'),
data: formData,
success: function(data) {
alert(data);
}
});
});
});
You can't post content to a different domain from javascript. It's not allowed for security reasons, and it is blocked by your browser.
When you execute an XmlHttpRequest by your webpage's javascript to another domain, your browser sets your domain name in the Origin header in your request. That target domain has to respond with a header called Access-Control-Allow-Origin containing your domain as well, meaning it allows your domain's clients to call it. Otherwise the browser will block the call.
Reference : https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
EDIT: There are ways to bypass the browser security and allow CORS all the time : https://www.thepolyglotdeveloper.com/2014/08/bypass-cors-errors-testing-apis-locally/

Showing a system error on a form after submitting via AJAX

I'm trying to find the best way in Angular to invalidate a form not due to a specific element, but due to a system-level error AFTER submission via AJAX. For example, you could put in a valid email and password (both good strings), press submit, and find out there is a system error that should trigger a generic error message on the form. Since this isn't tied to anything in the data model, what is the best way I can generically call the form 'invalid'?
<form name="loginForm" class="loginForm" ng-submit="loginSubmit(loginForm)">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email" ng-model="login.email" required>
<span class="error" ng-show="loginSubmit.email.$error">Required!</span><br>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="login.password" minlength="8" required>
</div>
<button type="submit" class="btn btn-default">Log In</button>
</form>
and...
ngModule.controller('LoginController', function($scope, $location) {
$scope['login'] = {};
$scope['loginSubmit'] = function(form) {
var loginPromise = myAsyncLoginFuncYouCanAssumeWorks();
loginPromise.done(function(){
$location.path('/');
});
loginPromise.fail(function() {
//how best to trigger a generic error in the form here?
});
};
});
As you can see, I'd like to trigger some form-wide error state after submission. It really could be as simple as adding an invalid form class to the form, but again, I'd like to know the purest Angular way to do this.
Add a label to your form with your generic error which shows upon a scope variable being true when an error occurs:
<div class="alert alert-danger" role="alert" ng-show="loginError">There was an error with your login details. Please check them and try again</div>
then when your promise fails:
loginPromise.fail(function () {
$scope.loginError = true;
});
maybe also could be nice if you have many system messages to abstract them all out into a separate service so you can inject the systemmessages service into your controller and then simply bind:
<div class="alert alert-danger" role="alert" ng-show="loginError">{{ systemMessages.loginError }}</div>
Alternatively as you use Bootstrap maybe inject the $modal service and show the error message inside a popup.
It is also important to make sure you try to use a bearer token stored in localatorage as oppose to cookies for persistence, so it doesn't get sent to the server on each request.
Anti forgery token would also be very beneficial for SPAs.
Your server could return some sort or error pay load with error key or error code.
{errors:[{key:"invalid.password"}]}
Assign the error response to your scope:
loginPromise.fail(function(response) {
$scope.errors = response.data;
});
Next, add filter to translate error key/code into error messages:
angular.module('mtApp').filter('errorFilter', function() {
return function(e) {
if (e === 'invalid.password') {
return 'Invalid password, please try again.';
}
};
});
Finally, display the appropriated errors as a list:
<div ng-show="errors">
<ul>
<li ng-repeat="e in errors">{{e.key | errorFilter}}</li>
</ul>
</div>
Optionally, you can reuse this same "$scope.erros" object combined to ng-class and control the CSS of each field with error.

Resources