I'm doing requests to my API server to authenticate a user, that's not the problem. The problem is that I don't know why my async function doesn't return anything, and I get an error because the data that I want from this function is undefined.
Don't worry if the error management is ugly and in general I can do this better, I'll do that after fixing this problem.
Utils.js class
async Auth(username, password) {
const body = {
username: username,
password: password
};
let req_uuid = '';
await this.setupUUID()
.then((uuid) => {
req_uuid = uuid;
})
.catch((e) => {
console.error(e);
});
let jwtData = {
"req_uuid": req_uuid,
"origin": "launcher",
"scope": "ec_auth"
};
console.log(req_uuid);
let jwtToken = jwt.sign(jwtData, 'lulz');
await fetch('http://api.myapi.cc/authenticate', {
method: 'POST',
headers: { "Content-Type": "application/json", "identify": jwtToken },
body: JSON.stringify(body),
})
.then((res) => {
// console.log(res);
// If the status is OK (200) get the json data of the response containing the token and return it
if (res.status == 200) {
res.json()
.then((data) => {
return Promise.resolve(data);
});
// If the response status is 401 return an error containing the error code and message
} else if (res.status == 401) {
res.json()
.then((data) => {
console.log(data.message);
});
throw ({ code: 401, msg: 'Wrong username or password' });
// If the response status is 400 (Bad Request) display unknown error message (this sould never happen)
} else if (res.status == 400) {
throw ({ code: 400, msg: 'Unknown error, contact support for help. \nError code: 400' });
}
})
// If there's an error with the fetch request itself then display a dialog box with the error message
.catch((error) => {
// If it's a "normal" error, so it has a code, don't put inside a new error object
if(error.code) {
return Promise.reject(error);
} else {
return Promise.reject({ code: 'critical', msg: error });
}
});
}
Main.js file
utils.Auth('user123', 'admin')
.then((res) => {
console.log(res); // undefined
});
Your Async function must return the last promise:
return fetch('http://api.myapi.cc/authenticate', ...);
or await the result and return it:
var x = await fetch('http://api.myapi.cc/authenticate', ...);
// do something with x and...
return x;
Notice that you donโt need to mix promise syntax (.then) with await. You can, but you donโt need to, and probably shouldnโt.
These two functions do exactly the same thing:
function a() {
return functionReturningPromise().then(function (result) {
return result + 1;
});
}
async function b() {
return (await functionReturningPromise()) + 1;
}
await is not to be used with then.
let data = await this.setupUUID();
or
let data=null;
setupUUID().then(res=> data = res)
I would try something like this:
const postReq = async (jwtToken) => {
const body = {
username: username,
password: password,
};
try {
const res = await fetch('http://api.myapi.cc/authenticate', {
method: 'POST',
headers: { "Content-Type": "application/json", "identify": jwtToken },
body: JSON.stringify(body),
})
if (res) {
if (res.status == 200) {
return res.json();
} else if (res.status == 401) {
const data = res.json();
console.log(data.message)
throw ({ code: 401, msg: 'Wrong username or password' });
} else if (res.status == 400) {
throw ({ code: 400, msg: 'Unknown error, contact support for help. \nError code: 400' });
}
}
} catch (err) {
console.error(err)
}
};
const Auth = async (username, password) => {
const jwtData = {
"origin": "launcher",
"scope": "ec_auth"
};
try {
const req_uuid = await this.setupUUID();
if (req_uuid) {
jwtData["req_uuid"] = req_uuid;
const jwtToken = jwt.sign(jwtData, 'lulz');
return await postReq(jwtToken);
}
} catch (err) {
console.error(err);
};
}
Related
ExampleObject is an example of what is expected from api endpoint
let ExampleObject={
"id":"",
"name":"",
"Body":""
}
how to make sure that the response has those keys without looping through the response? Response is a json object not an array
function handleError(err) {
//handling error code based
return err
}
export const GetUsers = async({
user,
Following
}, Paging) => {
try {
const response = await fetch(Apiurl, {
method: 'GET',
withCredentials: true,
credentials: 'include',
headers: {
'Authorization': Token,
'Content-Type': 'application/json'
}
})
if (response.status != 200) {
throw ('Error Sending request')
}
const ResponseArray = await response.json();
//Verify object here
if ('Key') {
throw ('error with json')
}
return ResponseArray
} catch (err) {
throw (handleError(err))
};
};
and then I call GetUsers from another module
async function callapi() {
try {
await GetUsers(Object, Paging)
} catch (err) {
}
}
You could use if ("id" in ExampleObject && "name" in ExampleObject && "Body" in ExampleObject) {}
This question already has answers here:
loging the response.json() inside the then() method before returning the response.json() promise
(1 answer)
JavaScript fetch - Failed to execute 'json' on 'Response': body stream is locked
(11 answers)
Closed 6 months ago.
I am trying to figure out what change I need to make to my flow where I am triggering a post call and on success return to run another fetch call followed by a 3rd party generated redirect (Stripe). At the moment I am receiving an error message at const session = await response.json()
with the message:
Failed to execute 'json' on 'Response': body stream already read
and I'm not sure how to refactor my code to be able to handle this issue. Can anyone point to what is wrong with my current setup and the rules I might be breaking?
const signUp = (e) => {
e.preventDefault();
if (password === passwordConfirmation) {
axios
.post(
"/api/auth/signup/",
{ email, password, passwordConfirmation },
{
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
}
)
.then((res) => {
const data = res.data;
setUser(data.user);
// Set the error message
if (data.message.status === "error") {
console.log("Error present");
setMessage(data.message);
}
return data.message.status;
})
.then(async (serverMessage) => {
// On successful sigin, redirect to /api/stripe/checkout-session/
if (serverMessage !== "error") {
// Get Stripe.js instance
const stripe = await stripePromise;
const response = await fetch("/api/stripe/checkout-session/", {
method: "POST",
});
console.log(`response: ${response.json()}`);
const session = await response.json();
console.log(`session: ${session}`);
// When the customer clicks on the button, redirect them to Checkout.
const result = await stripe.redirectToCheckout({
sessionId: session.sessionId,
});
// router.push('/api/stripe/checkout-session/')
// router.push('/app/feed/')
}
})
.catch((err) => {
console.log(err);
console.log(err.request);
console.log(err.message);
});
} else {
setMessage({
status: "error",
body: "Passwords do not match. Please try again.",
});
}
};
Do not call .json() twice.
change
console.log(`response: ${response.json()}`);
const session = await response.json();
to
const session = await response.json();
console.log(`response: ${session}`);
you can only read body stream once :) Try this
const signUp = (e) => {
e.preventDefault();
if(password === passwordConfirmation){
axios.post('/api/auth/signup/', { email, password, passwordConfirmation }, {
headers: {
'Content-Type': 'application/json'
},
withCredentials: true
}).then((res) => {
const data = res.data;
setUser(data.user)
// Set the error message
if(data.message.status === 'error'){
console.log('Error present')
setMessage(data.message)
}
return data.message.status
}).then(async (serverMessage) => {
// On successful sigin, redirect to /api/stripe/checkout-session/
if (serverMessage !== 'error'){
// Get Stripe.js instance
const stripe = await stripePromise;
const response = await fetch('/api/stripe/checkout-session/', { method: 'POST' });
const responseBody = await response.json()
console.log(`response: ${responseBody}`);
const session = responseBody;
console.log(`session: ${session}`);
// When the customer clicks on the button, redirect them to Checkout.
const result = await stripe.redirectToCheckout({
sessionId: session.sessionId,
});
// router.push('/api/stripe/checkout-session/')
// router.push('/app/feed/')
}
}).catch((err) => {
console.log(err)
console.log(err.request)
console.log(err.message)
})
} else {
setMessage({
'status': 'error',
'body': 'Passwords do not match. Please try again.'
})
}
}
I have don't the API check if the token is expired. I have to make a GET call, if I got the 403, error from the API, then I should re-login.
I attempted:
app.get = async (body) => {
return new Promise((resolve, reject) => {
let user = await user.findOne({
where: {
accountId: body.accountId
}
});
if(user){
body.accessToken = user.accessToken;
} else {
body.accessToken = await app.login();
}
request(
{
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + body.accessToken
},
method: 'GET',
uri: `${config.acs.url}${body.url}`,
json: true
}
)
.then((response) => {
resolve(response);
})
.catch((error) => {
// logger.info(error);
if(error.statusCode == 403){
body.accessToken = await app.login(); <<------------- ๐๐๐
app.get(body);
}
reject(error);
});
});
}
I don't know how else to avoid this error.
SyntaxError: await is only valid in an async function
I already have
app.get = async (body) => { ...
I need to re-login only when I get the 403 code in the error block.
How do I re-structure my code to achieve what I described?
The function used in the Promise is not an async function
Try this snippet
app.get = async (body) => {
let resolve, reject;
const promise = new Promise((re, rj) => {
resolve = re;
reject = rj;
});
let user = await user.findOne({
where: {
accountId: body.accountId
}
});
if(user){
body.accessToken = user.accessToken;
} else {
body.accessToken = await app.login();
}
request(
{
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + body.accessToken
},
method: 'GET',
uri: `${config.acs.url}${body.url}`,
json: true
}
)
.then((response) => {
resolve(response);
})
.catch(async (error) => {
// logger.info(error);
if(error.statusCode == 403){
body.accessToken = await app.login(); <<------------- ๐๐๐
app.get(body);
}
reject(error);
});
return promise;
}
I have a change password function that hits this api for verification and I want to display an error if the current password is incorrect.
Any direction on how to go about this or if what I'm doing makes no sense please point me in the right direction if would be so kind, it would be greatly appreciated!
case "PUT":
try {
const validContact = await Contact.findOne({ _id: req.body.id });
const valid = bcrypt.compareSync(
req.body.currentPassword,
validContact.password
);
if (valid) {
const hashedPassword = bcrypt.hashSync(
req.body.newPassword,
bcrypt.genSaltSync()
);
const contact = await Contact.findOneAndUpdate(
{ _id: req.body.id },
{ password: hashedPassword },
{ new: true }
);
res.status(200).json({ success: true, data: contact });
}
res.status(400).json({ success: false });
} catch (error) {
res.status(400).json({ success: false });
}
break;
This is the function that calls the API upon form submission
const submitNewPassword = (submitNewPasswordForm, resetForm) => {
submitNewPasswordForm(); // <-- I want to put this in a conditional
resetForm();
setOpenPasswordPopup(false);
setNotify({
isOpen: true,
message: "Password updated successfully",
type: "success",
});
};
edit: submitNewPassword function
const submitNewPasswordForm = async () => {
try {
const res = await fetch(`${process.env.APP_DOMAIN}/api/${apiRoute}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(values),
});
router.push(`${process.env.APP_DOMAIN}/`);
} catch (error) {
console.log(error);
}
};
Your submitNewPasswordForm doesn't return anything right now (well, it does, but it's just an empty Promise). To be able to check if it was a good request or not, you need to return something from it. Example:
const submitNewPasswordForm = async () => {
try {
const res = await fetch(`${process.env.APP_DOMAIN}/api/${apiRoute}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(values),
});
// this check is also necessary; 400 isn't an exception that would get caught
if (!res.ok) {
throw new Error(res.statusText)
}
router.push(`${process.env.APP_DOMAIN}/`);
return true;
} catch (error) {
console.log(error);
// you could also re-throw the error, or return something else
return false;
}
};
Because it's an async function, it returns a Promise, so you need to get the return value out with a .then:
const submitNewPassword = (submitNewPasswordForm, resetForm) => {
submitNewPasswordForm()
.then((ok) => {
if (!ok) {
// show error
} else {
resetForm();
setOpenPasswordPopup(false);
setNotify({
isOpen: true,
message: "Password updated successfully",
type: "success",
});
}
})
};
If you re-throw the error in the first function, you could .catch it rather than checking for an ok value. You could also make the second function an async function if you wanted to. Example:
const submitNewPassword = async (submitNewPasswordForm, resetForm) => {
try {
submitNewPasswordForm()
// rest of the code
} catch (err) {
// show error
}
}
Whichever way you go, you'll have to return something from the function in order to know if it was a success or not.
You will need to declare the submitNewPassword with async as you will require to await.
Then I added the await before the submitNewPasswordForm() and the happy and unhappy path.
You can check the result here.
const submitNewPassword = async (submitNewPasswordForm, resetForm) => {
const response = await submitNewPasswordForm(); // <-- I want to put this in a conditional
if(response.status === 200){
const body = await response.json()
//happy path
}else
{
const bodyError = await response.json()
//unhappy path
}
resetForm();
setOpenPasswordPopup(false);
setNotify({
isOpen: true,
message: "Password updated successfully",
type: "success",
});
};
I have fetch, it throws error:
fetchAuthorization(username, password) {
return fetch(`https://api.github.com/user`, {
method: 'GET',
headers: {
"Accept": 'application/json',
"Content-Type": 'application/json',
"Authorization": "Basic " + btoa(`${username}:${password}`)
},
})
.then(res => {
if(res.status !== 200) {
throw Error("Bad validation");
}
return res.json();
});
},
then this async action (redux):
export const onSignInAction = (username, password) => {
return dispatch => {
return api.fetchAuthorization(username, password)
.then( res => {
dispatch(signInAction(username, password, res));
})
.catch(err => console.log(err));
}
}
next:
handleSignIn = (username, password) => {
const { onSignInAction } = this.props;
onSignInAction(username, password);
}
And now I want catch Error from my fetch :
handleSignIn = () => {
const { onSignIn } = this.props;
const { errorMessage, open } = this.state;
const username = this.usernameField.getValue();
const password = this.passwordField.getValue();
try {
onSignIn(username, password);
}
catch (Error) {
this.setState({
errorMessage: 'Incorrect username or password'
});
}
}
How to catch it correctly? My code doesn't do this stuff. Thanks!
You can throw error from .catch(), substitute Promise.prototype.catch() for try..catch
var onSignInAction = () => {
return Promise.reject(new Error("Bad validation")).catch(e => {
console.log("catch 1", e.message);
throw e
});
}
onSignInAction()
.catch(err => {
console.log("catch 2:", {
errorMessage: 'Incorrect username or password'
}, err.message);
});