API Post from Vue to Cake Backend fails on reload - vue.js

API Post from Vue to Cake Backend fails in certain conditions of reloading FrontEnd, BackEnd, browser and/ or Computer..
I am developing an app with Vue.js as Frontend Framework and CakePHP 3.8 as Backend Framework. I have set up a few Forms in Vue from which I am making POST Requests to several Cake Backend Endpoints. My GET requests are working fine..
In certain conditions I get an error message. This error message disappears once I remove some code, try again, and then put back the original code and try again.. Error message says there is a CORS block, but it says so with all errors, also with spelling errors. So the error message is totally in this case. So with exactly the same code the request works after removing, retrying, and putting back..
Root Cause & Solution
Important thing helping me in finding the solution was realising that the Console was giving the wrong error (CORS). The error log in CakePHP was a lot clearer. This one gave an error Error: [PDOException] SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null (Cake error.log). So the data written to database could not be saved to the database.. The error was in handling the POST data on the Cake Backend.
The solution of the problem was to first perform the request data check before making the save.. if($data){}
***What I think is the problem is connected to the time lag in establishing first connection with the Back-End.. Wants to immediately make the save without having established the data.. Apparently this only happens when starting up the system. ***
Does anyone have a better explanation for this?
Vue Front-End Code:
<template>
<div id="addpacklist">
{{post_data}}
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<form v-on:submit.prevent="addPackinglist">
<h2>Add PackingList</h2>
<label for="name">Name:</label>
<input type="text" id="name" v-model="name"><br>
<input type="submit" value="Submit">
</form>
</div>
</template>
<script>
var data = [];
import $ from 'jquery';
// you will use v-model & data on edits..
export default{
name :'addpacklist',
data(){
return{
errors: [],
name: '',
post_data: '',
}
},
methods: {
addPackinglist(){
this.errors = [];
// form validation
if(!this.name){
this.errors.push('Name required.');
}
if (this.name.length > 10){
this.errors.push('Name maximum 10 Characters');
}
if(this.name && this.name.length < 11){
// cannot use associative arrays in Javascript : does not exist!!
data[0] = this.name;
console.log(data);
fetch('http://wampprojects/holidays_backend/packinglists/add', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(json_data => this.post_data = json_data)
.catch(error => {
console.log("error");
});
}
},
},
}
</script>
Cake Back-End Code:
public function add(){
$data = $this->request->data;
$sendback = "";
// build up data object and save to database
$packinglist = $this->Packinglists->newEntity();
// if you do not perform this check you will get an error
// I think it has something to do with establishing the first link from Front-End to Back-End (Session)
// THIS IS WHAT I ADDED TO MAKE IT WORK!!
if($data){
$packinglist->name = $data[0];
// something wrong with saving entity
if($this->Packinglists->save($packinglist)){
$sendback = "save success";
}else{
$sendback = "save failed";
};
}
// no automatic view, only data returned
$this->autoRender = false;
return $this->response
->withType('application/json')
->withStringBody(json_encode($data));
}
Some scenario’s I logged..
When stopping one element and keeping browser on..
**When stopping & restarting the local dev server for Vue it still works. (ok)
**When stopping & restarting internet still works. (ok)
**When stopping & restarting wamp server still works.. (ok)
**When stopping & starting wamp server still works.. (ok)
When stopping one element & shutting browser down..
**Stop Wamp & restart (ok)
**Stop Wamp & start (ok)
**Stop Vue & restart (ok)
**Stop Internet & restart (ok)
When stopping & (re)starting Vue & Wamp & keeping browser on..
**When first restarting Wamp, then Vue.. (ok)
**When first starting back up Wamp, then Vue. (ok)
**When first restarting Vue with “npm run serve”.. Then start all services wamp.. (nok)
**When first restarting Vue.. Then restart all services wamp.. (ok)
When stopping & (re)starting Vue & Wamp & shutting browser off..
**First restarting Wamp, then Vue, then browser.. (nok)
**First start Wamp, then Vue, then browser.. (ok)
**First start Vue, then restart Wamp, then browser (ok)
**First start Vue, then start Wamp, then browser (nok)
When shutting down computer
**First start up Vue, then Wamp, then browser (nok)
**First start up Wamp, then Vue, then browser (nok)

Root Cause & Solution
Important thing helping me in finding the solution was realising that the Console was giving the wrong error (CORS). The error log in CakePHP was a lot clearer. This one gave an error Error: [PDOException] SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null (Cake error.log). So the data written to database could not be saved to the database.. The error was in handling the POST data on the Cake Backend.
The solution of the problem was to first perform the request data check before making the save.. if($data){}
Thank you -ndm- for the in detail explanation
The resulting error can/will (depending on your PHP installation's output_buffering setting) immediately produce output, in which case PHP cannot send any headers anymore, ie no CORS realated headers that would be required for CORS request to work can be sent. Depending on the exact error message and the additional context that is attached to it (stacktrace etc), the output buffer might not overflow on every single request (a single byte can already make the difference), hence that kind of unrealiable behavior.

Related

Google Login fails: either I get an error about an unlisted "redirect_uri" value or I get redirected to localhost which also fails

I am trying to enable users to log into my page using Google Social Sign On.
This worked in development and now I am going to production with it. It is a resume project.
To start the login process I click an anchor tag which redirects me to https://www.example.com/api/auth/google which Nginx sends to my backend endpoint: this.router.get("/google", googleAuth); which performs some magic to redirect the user to a page that formerly showed my 3 Google accounts.
This page fails and says "Access blocked: This app’s request is invalid"
I click to see error details and get
Error 400: redirect_uri_mismatch
You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy.
If you're the app developer, register the redirect URI in the Google Cloud Console.
Request details: redirect_uri=http://localhost:8000/auth/google/callback
I am expecting it to redirect to https://www.example.com/api/auth/google/callback
In my Google console for this application I click under OAuth 2.0 Client IDs and see that my "Authorized redirect URIs" lists one singular entry where I expect to look and see "http://localhost:8000/auth/google/callback" : but it says https://www.example.com/api/auth/google/callback
It's like OK why not add the localhost:8000 value to the redirect URIs? Ok let's try it
Now I try clicking the Google button, and get redirected to a list of my available google accounts as I want. But now when I click one of them to be logged in, I get redirected to localhost, which is not what I want, I want my production URL:
This site can’t be reached
localhost refused to connect.
So what I want is for the redirect to the login page to come from my "example.com/api/auth/google" URL, and for the redirect to be sent to "example.com.api/auth/google/callback"
How can I make this happen? I think it requires nginx configured somehow.
edit to include code:
Here are the relevant controller endpoints
this.router.get("/google", googleAuth);
this.router.get("/google/callback", googleAuthCallback, this.grantAccessToken.bind(this));
googleAuth and googleAuthCallback:
export const googleAuth = passport.authenticate("google", { scope: ["email", "profile"], successRedirect: "http://localhost:3000" });
export const googleAuthCallback = passport.authenticate("google", { session: false });
My client side code that starts the redirect:
const GoogleButton: React.FC<{}> = () => {
const startGoogleSignInFlow = getEndpoint("/auth/google");
// the value is https://www.apartmentsneargyms.com/api/auth/google
return (
<a href={startGoogleSignInFlow} className="flex items-center">
<div className="w-full h-12 flex justify-center rounded-lg googleBtn">
<button className="flex items-center mr-4">
<img alt="google icon" src={GoogleIcon} height={24} width={24} className="mr-2" />
Google
</button>
</div>
</a>
);
};
I almost exclusively followed this guide to get here.
My nginx configuration proxies www.example.com/api to the backend server like this:
location /api/ {
proxy_pass http://localhost:8000/;
}
I'm suspicious the problem is that the Google services are hearing from me in the form of a request that starts with localhost:8000 because the redirects come from that location on my remote machine's network.
edit2: I want to say that I had this working on my local machine using localhost. It's deploying to prod that's difficult here.
Found the source of the problem. Linda Lawton - DaImTo was right to tell me to post my code. I found this in my passport config:
const passportConfig = (passport: any) => {
passport.use(
new GoogleStrategy(
{
clientID: googleClientId,
clientSecret: googleClientSecret,
callbackURL: "http://localhost:8000/auth/google/callback",
passReqToCallback: true,
},
// snip
}
On the following line:
export const googleAuth = passport.authenticate("google", { scope: ["email", "profile"], successRedirect: "http://localhost:3000" });
You've your local env URL hardcoded (http://localhost:3000), this value must be different in dev and production.

Nuxt3 useFetch only retrieving data occasionally

I'm a little bit confused with Nuxt 3 and the lifecycle of when it gets data. I understand that it's a universal rendering process, but I'm using Strapi 4 to manage content in my Nuxt 3 project and only occasionally do I retrieve the data via useFetch. The API route from Strapi never goes down so I'm probably just doing something wrong.
Here is my Vue file in Nuxt:
<script setup lang="ts">
const {data: works, pending, error} = await useFetch("http://localhost:1337/api/works", {
params: {
populate: "*"
}
});
</script>
<template>
<div>
<div v-for="work in works">
... do something
</div>
</div>
</template>
I'm not sure how to get the content when the page loads. When I log the error returned, it's just true. But it's only sometimes. The content will load once, and then as soon as I refresh the page, it goes back to having an error. So I'm thinking something is getting cached maybe client-side? I'm really not sure what to do next.
Try passing this option to useFetch: initialCache: false. See more

catch errors for firebase login and display to the user with two-way binging not working

I have a very simple straight-forward login template, and I'm using the Firebase to centralize login options.
[part1] Problem is, for some reason, most of the browser except Chrome - receiving a error:
This browser is not supported or 3rd party cookies and data may be disabled
Do I have any firebase configuration issues or something ? because I don't see any posts about these issues - and I can't be the only one experiencing this... ( only Chrome works w/o issues )
I can't start telling people to configure their browser?! ...
[part 2] at least, I'm trying to catch those errors - and display them to the user, otherwise, he has no idea what's the problem and it seems that the app isn't doing anything ( popup opens then close and that's it )
login.ts
verifyErrorMsg() {
console.log("this.loginError:", this.loginError);
}
loginWithGoogle() {
this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider())
.then(userData => {
console.log("success - moving to main page");
}).catch(error => {
this.loginError = error;
this.verifyErrorMsg(); // show the real error, but in console
});
}
^^^ there's no this issue here .. verified that this is the real object.
template.html
<button ion-button (click)="verifyErrorMsg()">
Check login error value
</button>
<p color="danger">
{{ loginError.message }}
</p>
The funny/strange part here is, when I click on my test button to verify what's the real value of this very moment - then suddenly the <p> gets update, even though the function verifyErrorMsg only prints to console ...
I guess it's a cycle or digest kind of thing .. but I'm not sure what needs to be done here ... ?

Meteor IronRouter onBeforeAction causes exception in defer callback

I'm trying to secure my meteor app with iron-router, here's my onBeforeAction function:
this.route('manageUsers', {
path: '/panel/user_management',
layoutTemplate: 'panel',
onBeforeAction: function(){
if((Meteor.user() === null)||(Meteor.user().role !== 'superAdmin')){
Router.go('signIn');
throwAlert('You dont have access to see this page', 'notification');
}
}
});
When I'm trying to go to /panel/user_management subpage by pressing a link button everything goes fine (user is redirected etc.), but when I type the path directly in my browser (localhost:3000/panel/user_management) and hit enter user is not getting redirected and I receive in console Exception in defer callback error. Anyone know what I'm doing wrong?
For additional information, this view lists me all users registered. When I go to this path normally (without error) I see complete user list. When I receive error template doesn't appear in > yield.
Finally, I've solved this - the problem was wrong if statement, here's the correct one:
if((!Meteor.user())||(Meteor.user().role !== 'superAdmin')){}

Redirect Express js piped (req.pipe) res properly

Probably I'm doing something wrong but:
In my browser I have a code
window.location = '/some/url'
or element with href="/some url"
in node (express.js) I have following route handler:
app.get('/some/url', function(req, res){
res.redirect('http://www.google.com')
});
Content of the page is loaded with strange flaw Google page. And address bar is not changed (http://localhost:3000/some/url).
I'm making request via intermediate server using
req.pipe(request(host + req.url)).pipe(res) and it seems that piped res does not redirect properly. Any ideas how to solve it?
I needed to use {folllowRedirect: false} option for reqest.js