axios post to find out if data is valid - laravel

OK I am at the end of my day and I am not thinking straight. So this is what I have...
a Laravel controller, send it a username, and it tells me if the username is available, and if it isnt, it gives me a 422 code
public function checkUsername(Request $request) {
Validator::make($request->all(), [
'name' => ['required', 'string', 'max:255', 'unique:users'],
])->validate();
return response()->json([
'valid' => true,
'data' => [
'message' => 'Username is available!'
]
], 200);
}
Example of valid response:
{"valid":true,"data":{"message":"Username is available!"}}%
The curl to test is:
curl -X POST -H "Content-Type: application/json" -d '{"name": "bossryan"}' http://127.0.0.1:8000/api/checkusername
Next: I have a frontend Vue using Vee-validate. It does a bunch of things, but I need to add this latest validation into the mix, so if the username is taken (I don't get the valid response from above, it needs to reply with "This username is already taken"
validateUsername(value) {
// if the field is empty
if (!value) {
return 'This field is required';
}
const regex = /^[a-zA-Z0-9_.+-]{4,20}$/i;
if (!regex.test(value)) {
return 'This must be a minimum of 4 characters';
}
return true;
},
This is the axios I created but it isnt working:
const isUnique = (value) => {
return axios.post('/api/checkusername', { email: value }).then((response) => {
// Notice that we return an object containing both a valid property and a data property.
return {
valid: response.data.valid,
data: {
message: response.data.message
}
};
});
};
I know I need to add in axios, but I am just having a heck of a time setting it up and my mind keeps rushing around. I am just looking for someone who can just help me plug in the axios request above //All is good, so I can finish this up.
THANKS FOR THE HELP COMMUNITY!

Vee-validate seems to want a resolved promise for async validation. Axios will reject the promise if the status is >= 400 so you need to handle that accordingly.
Assuming when validation fails that the response body matches the same { valid, data: { message } } format, you'd want something like the following
const isUnique = (name) =>
axios.post("/api/checkusername", { name })
.then(({ data }) => data)
.catch(err => ({ // resolve with error details
valid: err.response?.data?.valid ?? false,
data: {
// get the message from the response if it exists
message: err.response?.data?.data?.message ?? "Validation failed"
}
}));
export default {
methods: {
async validateUsername(value) {
// do your synchronous checks as per question
const check = await isUnique(value);
return check.valid || check.data.message;
}
}
}
This will provide a generic message "Validation failed" if the 422 response body doesn't match expectations.

Related

Cannot catch error in promise chaining Larave-vue2 SPA authentication

I try to authenticate in Laravel9 Sanctum a SPA using vue3 and vuex, not with the token authentification but with the SPA Authentication.
I am not very used to the javascript language, and even less to promise chaining.
The first thing I am trying is registration.
Here are my methods.
The backend registration method
public function register(Request $request)
{
$request->validate([
'name' => 'required|string',
'email' => 'required|email|string|unique:users,email',
'password' => [
'required',
'confirmed',
Password::min(8)->mixedCase()->numbers()->symbols()
]
]);
$user=User::create([
'name' => $request['name'],
'email' => $request['email'],
'password' => bcrypt($request['password'])
]);
return response (['user'=>$user]);
}
The frontend registration method in the register.vue
//is actually the register form's data
const user = {
name: "",
email: "",
password: "",
password_confirmation: "",
};
function register(ev) {
ev.preventDefault();
store
.dispatch("register", user)
.then((data) => {
console.log("data in vue");
console.log(data);
router.push({
name: "Login",
});
})
.catch((error) => {
if (error.response.status === 422) {
errors = error.response.data.errors;
}
});
}
the actions method in the store/index.js
actions: {
register({ commit }, form) {
console.log("in register of index");
axiosClient.get("/sanctum/csrf-cookie");
return axiosClient.post("/api/register", form).then(({ data }) => {
console.log("data dans index");
console.log(data);
return data;
});
},
...
The registration is working fine but when I try an already existing email in the registration form, I get a status 422 as expected and this response from the axiosClient.post('/api/register',form):
{"message":"The email has already been
taken.","errors":{"email":["The email has already been taken."]}}
I expect this error to be intercepted by the catch in the register view but it doesn't happen. Despite this error I continue to use the .then and to push the Login route which is not what I want.
Can somebody tell me where I am doing wrong ?
I completely forget that in my axios.js there were interceptors that triggered this wrong behavior.
All is clear now.

how can I customize json responses of laravel api and show them in vuex

Trying different solutions, I am fooling around with
response()->json([ ])
To create responses that I can read in my vue / vuex application
The Laravel api function that stores a new Speler ( dutch for player ;)):
I have trouble sending the created, or found Speler-object, through the response to the vuex-store.
Tried to set the status to 202 when succesfully logged, yet the actual status sent is 200..
It is clear that I do not understand it well enough. Can anyone help and explain?
public function store(Request $request)
{
if (Game::where('id',$request['game_id'])->exists() ){
if (!Speler::where('name',$request['name'])->where('game_id',$request['game_id'])->exists()){
$newSpeler = Speler::create(
[
'name' => $request['name'],
'pass_code' => $request['pass_code'],
'game_id' => $request['game_id']
])->first());
return $newSpeler;
}
elseif ( Speler::where('name',$request['name'])->where('game_id',$request['game_id'])->where('pass_code', $request['pass_code'])->exists()){
$speler = Speler::where('name',$request['name'])->where('game_id',$request['game_id'])->where('pass_code', $request['pass_code']);
return response()->json(['speler'=> $speler, 202]);
}
return response()->json(['status' => 'This name is already used, pass-code is not correct', 409]);
}
return response()->json([ 'status' => 'The game-pin does not exist', 403 ]);
}
This is called form the vuex actions:
export const addSpeler = ({commit}, formData) => {
return new Promise((resolve, reject) => {
fetch(`api/speler`, {
method: 'post',
body:formData,
})
.then(res => {
if (res.status === 202){
resolve('De speler is succesfully logged on');
commit('SET_CURRENT_SPELER', res.data.speler);
}
else if (res.status === 201){
commit('SET_CURRENT_SPELER', res.data);
resolve('De speler is succesfully added')
}
else {
reject('De speler is not logged in. Name exists and does not match passcode');
}
})
.catch(err => {
reject(err.message)
});
})
}
and this is called from a vue method:
methods: {
addSpeler(){
this.errorMessage ='';
this.spelerAdded =false;
const formData = new FormData();
formData.append('name', this.name);
formData.append('pass_code',this.pass_code);
formData.append('game_id', this.currentGame.id);
this.$store.dispatch('addSpeler', formData )
.then(res => {
this.spelerAdded = true;
console.log(res.status);
})
.catch(err => {
this.errorMessage = err;
this.spelerAdded = false;
});
},
mutations.js:
export const SET_CURRENT_SPELER = (state, speler) => {
state.currentSpeler = speler;
}
state.js:
export default{
currentGame:{},
currentSpeler:{}
}
The comment by porloscerros answered the question perfectly :
the status goes as the second argument of the json method return response()->json(['speler'=> $speler], 202); (and not inside the array as you are doing). If you don't pass a second argument, the argument value is assigned to 200 by default json(mixed $data = [], int $status = 200, array $headers = [], int $options = 0)

PayPalRestSDK: Mock Response Instrument_Declined response when capturing order

I've been struggling to mock the 'INSTRUMENT_DECLINED' issue, which according to Paypal is an usual error after attempting to capture an order (https://developer.paypal.com/demo/checkout/#/pattern/server).
Correctly setting the header to mock the response will help me to simulate other errors from the docs https://developer.paypal.com/docs/business/test-and-go-live/simulation-tests/#orders.
I'm using Laravel back end and React front end.
I have already gotten the expected mocked response using axios and calling in the front-end:
return axios
.post(
`https://api-m.sandbox.paypal.com/v2/checkout/orders/${orderID}/capture`,
{},
{
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + accessToken,
"PayPal-Mock-Response":
'{"mock_application_codes":"INSTRUMENT_DECLINED"}',
},
}
)
.then((res) => {
console.log(res);
})
However, I don't want to expose accessToken into the front end, I'd prefer to do everything in the back-end with GuzzleHtttp.
I've already had a positive response - with 'status': 'COMPLETED' without adding PayPal-Mock-Response header like so:
$client = new \GuzzleHttp\Client();
$response = $client->request(
'POST',
'https://api-m.sandbox.paypal.com/v2/checkout/orders/' . $paypalOrderId . '/capture',
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $access_token,
// 'PayPal-Mock-Response' => json_encode(["mock_application_codes" => "INSTRUMENT_DECLINED"]),
// 'PayPal-Mock-Response' => "['mock_application_codes':'INSTRUMENT_DECLINED]",
],
],
);
$data = json_decode($response->getBody(), true);
But when adding the PayPal-Mock-Response header like shown in the commented code from above, both attemps have returned this exception:
"message": "Client error: POST https://api-m.sandbox.paypal.com/v2/checkout/orders/9HG50866FU785784C/capture
resulted in a 404 Not Found response",
"exception": "GuzzleHttp\Exception\ClientException",
"file": "C:\xampp\htdocs\react\React-Laravel\vinos-gdl\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php",
This exception, I'm sure has to do to the way I'm passing the PayPal-Mock-Response header into the Guzzle http call but I can't find the way to do it correctly.
Any help is much appreciated.
UPDATE SOLVED**
Ok the way to go is sending the header like so:
'PayPal-Mock-Response' => json_encode(["mock_application_codes" => "INSTRUMENT_DECLINED"]),
I dunno if I had any typo but I finally got it. I also used INTERNAL_SERVER_ERROR to get a 500 response from server and built a catch for every situation:
} catch (ClientException $e) {
if ($e->hasResponse()) {
return response()->json([
'msg' => 'Client Exception',
'error' => json_decode($e->getResponse()->getBody()),
], 400);
}
return response()->json([
'msg' => 'Client Exception',
'request' => $e->getRequest(),
$e->hasResponse() ? $e->getResponse() : ""
]);
// return response()->json(['msg' => 'Server Error', 'error' => report($e)]);
} catch (BadResponseException $e) {
if ($e->hasResponse()) {
return response()->json([
'msg' => 'Uknown Exception',
'error' => json_decode($e->getResponse()->getBody()),
], 500);
}
return response()->json([
'msg' => 'Uknown Exception',
'request' => $e->getRequest(),
$e->hasResponse() ? $e->getResponse() : ""
]);
}
Thanks for the ones who commented.
"PayPal-Mock-Response":
'{"mock_application_codes":"INSTRUMENT_DECLINED"}',
...
// 'PayPal-Mock-Response' => "['mock_application_codes':'INSTRUMENT_DECLINED]",
There is a difference betwen {} (JSON object) and [] (JSON array).
What you have inside the brackets is not an array, so its syntax is invalid. It doesn't map to anything PayPal expects, hence the 404.

How to store validator errors into an object with Laravel and Vue.js

I am using Laravel 7 and Vue.js 2.
I made a form that should show a table if every field has been inserted.
I made also a server-side validation to check if all fields are correctly inserted.
This is the function that creates a call to the server with Axios:
runReport: function() {
const url = "api/get_report?room="+this.formReport['room']+"&participant="+this.formReport['participant']+"&start="+this.formReport['start']+"&end="+this.formReport['end'];
axios.get(url)
.then((response) => {
console.log(response.data.data);
this.meetingsReport = response.data.data;
alert('viva');
this.$emit('passMeetings', this.meetingsReport);
this.$emit('success');
})
.catch(function(error) {
console.log(error.response.data);
this.errors = error.response.data; //problem
alert('noviva');
});
}
This is the validator in the controller:
$validator = Validator::make($request->all(), [
'room' => 'required',
'start' => 'required',
'end' => 'required',
'participant' => 'required',
]);
if ($validator->fails()) {
return response($validator->errors(), 422);
}
If everthing has been correctly inserted in the form I have no problems, but if I missed a field I am unable to store the errors in an empty object that I created called errors.
UPDATED: This is the response from the validation failed (status 422):
{
"room": [
"The room field is required."
],
"participant": [
"The participant field is required."
]
}
I suppose the problem is that I am unable to access to this.errors from the catch block of Axios.
this in the context of an anonymous function doesn't point to the vue instance, so you may need to bind this to the function or use arrow function within the catch block
.catch(error => {
console.log(error.response.data);
this.errors = error.response.data; //should not be any - problem
alert('noviva');
});
At the end I solved using the keyword self in the axios call. In this way I was able to connect the catch block with Vue.js component.
This is the code:
runReport: function() {
let self = this;
const url = "api/get_report?room="+this.formReport['room']+"&participant="+this.formReport['participant']+"&start="+this.formReport['start']+"&end="+this.formReport['end'];
axios.get(url)
.then((response) => {
console.log(response.data.data);
this.meetingsReport = response.data.data;
alert('viva');
this.$emit('passMeetings', this.meetingsReport);
this.$emit('success');
this.errors = {};
})
.catch(function(error) {
console.log(error.response.data);
self.errors = error.response.data;
self.$emit('failure');
});
}

Laravel Validation with vue js

i want to post ajax request using vue-resource this.$http.post request. it worked perfectly fine if i passed all validation rules but i want to get some validations if it fails. so far i keep getting 500 error if i don't fill out some input fields. it's hard for me to debug the error because it didn't appeared on the network tab.
here's what i've done so far
//my modal component
<script>
export default {
props: ['show'],
data() {
return {
input: {
id: '',
name: '',
address: '',
email: ''
},
errorInputs: {}
}
},
methods: {
createStudent() {
this.$http.post('/students', this.$data.input)
.then((response) => {
alert('added new row!)
}, (response) => {
console.log(response.data);
});
}
}
}
</script>
// my controller
public function store(Request $request) {
$validator = $this->validate($request,[
'id' => 'required',
'name' => 'required|unique:students',
'email' => 'required|unique:students|email',
'address' => 'required',
]);
if($validator->passes()){
Student::create($request->all());
return response()->json([], 201);
}
$errors = json_decode($validator->errors());
return response()->json([
'success' => false,
'message' => $errors
],422);
}
any helps and references would be appreciated. i am using laravel 5.3 and vue js 2
$this->validate() returns 422 error response alongside your validation errors, so you should get those errors in then() second callback (like you do now). Your vue component body should be like this:
{
data() {
// ...
},
createStudent() {
this.$http
.post('/students', this.input)
.then(this.handleSuccess, this.handleError)
},
handleSuccess(res) {
alert('student created')
},
handleError(res) {
if (res.status === 422) {
this.errorInputs = res.body
} else {
alert('Unkown error!')
}
}
}
Remember to add v-model="input.fieldName" properties to your inputs.
Remember to include your session token along with your post, unless of course you are disabling csrf tokens for that route.
Since Laravel 5.1 you can disable this in your verifytoken middleware
<?php namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as ...
class VerifyCsrfToken extends ... {
protected $except = [
'payment/*',
];
}

Resources