How catch error from async action with redux? - javascript

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);
});

Related

Failed to load resource: the server responded with a status of 400 (Bad Request) http://localhost:5000/api/refresh_token

Im getting this error, but I don't know what's wrong with my code, since it works perfectly on Postman, but doesn't when I run it on Node.js or localhost:3000 (which is my client side).
Here's the controller authCtrl.js code:
const Users = require("../models/userModel");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const authCtrl = {
register: async (req, res) => {
try {
const { fullname, username, email, password, gender } = req.body;
let newUserName = username.toLowerCase().replace(/ /g, "");
const user_name = await Users.findOne({ username: newUserName });
if (user_name)
return res.status(400).json({ msg: "This user name already exists." });
const user_email = await Users.findOne({ email });
if (user_email)
return res.status(400).json({ msg: "This email already exists." });
if (password.length < 6)
return res
.status(400)
.json({ msg: "Password must be at least 6 characters." });
const passwordHash = await bcrypt.hash(password, 12);
const newUser = new Users({
fullname,
username: newUserName,
email,
password: passwordHash,
gender,
});
const access_token = createAccessToken({ id: newUser._id });
const refresh_token = createRefreshToken({ id: newUser._id });
res.cookie("refreshtoken", refresh_token, {
httpOnly: true,
path: "http://localhost:5000/api/refresh_token",
maxAge: 30 * 24 * 60 * 60 * 1000, // 30days
});
await newUser.save();
res.json({
msg: "Register Success!",
access_token,
user: {
...newUser._doc,
password: "",
},
});
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
login: async (req, res) => {
try {
const { email, password } = req.body;
const user = await Users.findOne({ email }).populate(
"followers following",
"avatar username fullname followers following"
);
if (!user)
return res.status(400).json({ msg: "This email does not exist." });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch)
return res.status(400).json({ msg: "Password is incorrect." });
const access_token = createAccessToken({ id: user._id });
const refresh_token = createRefreshToken({ id: user._id });
res.cookie("refreshtoken", refresh_token, {
httpOnly: true,
path: "http://localhost:5000/api/refresh_token",
maxAge: 30 * 24 * 60 * 60 * 1000, // 30days
});
res.json({
msg: "Login Success!",
access_token,
user: {
...user._doc,
password: "",
},
});
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
logout: async (req, res) => {
try {
res.clearCookie("refreshtoken", {
path: "http:/localhost:5000/api/refresh_token",
});
return res.json({ msg: "Logged out!" });
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
generateAccessToken: async (req, res) => {
try {
const rf_token = req.cookies.refreshtoken;
if (!rf_token)
return res.status(400).json({ msg: "Refresh token failed." });
jwt.verify(
rf_token,
process.env.REFRESH_TOKEN_SECRET,
async (err, result) => {
if (err) return res.status(400).json({ msg: "JWT Verify failed." });
const user = await Users.findById(result.id)
.select("-password")
.populate(
"followers following",
"avatar username fullname followers following"
);
if (!user)
return res.status(400).json({ msg: "This does not exist." });
const access_token = createAccessToken({ id: result.id });
res.json({
access_token,
user,
});
}
);
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
};
const createAccessToken = (payload) => {
return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: "1d",
});
};
const createRefreshToken = (payload) => {
return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
expiresIn: "30d",
});
};
module.exports = authCtrl;
Here's the util fetchData.js (where I use axios to fetch the data):
import axios from "axios";
export const getDataAPI = async (url, token) => {
const res = await axios.get(`http://localhost:5000/api/${url}`, {
headers: { Authorization: token },
});
return res;
};
export const postDataAPI = async (url, post, token) => {
const res = await axios.post(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const putDataAPI = async (url, post, token) => {
const res = await axios.put(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const patchDataAPI = async (url, post, token) => {
const res = await axios.patch(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const deleteDataAPI = async (url, token) => {
const res = await axios.delete(`http://localhost:5000/api/${url}`, {
headers: { Authorization: token },
});
return res;
};
Here's the redux action authAction.js file:
import { GLOBALTYPES } from "./globalTypes";
import { postDataAPI } from "../../utils/fetchData";
export const login = (data) => async (dispatch) => {
try {
dispatch({ type: GLOBALTYPES.ALERT, payload: { loading: true } });
const res = await postDataAPI("login", data);
dispatch({
type: GLOBALTYPES.AUTH,
payload: {
token: res.data.access_token,
user: res.data.user,
},
});
localStorage.setItem("firstLogin", true);
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
success: res.data.msg,
},
});
} catch (err) {
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
error: err.response.data.msg,
},
});
}
};
export const refreshToken = () => async (dispatch) => {
const firstLogin = localStorage.getItem("firstLogin");
if (firstLogin) {
dispatch({ type: GLOBALTYPES.ALERT, payload: { loading: true } });
try {
const res = await postDataAPI("refresh_token");
dispatch({
type: GLOBALTYPES.AUTH,
payload: {
token: res.data.access_token,
user: res.data.user,
},
});
dispatch({ type: GLOBALTYPES.ALERT, payload: {} });
} catch (err) {
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
error: err.response.data.msg,
},
});
}
}
};

Nodejs async loop function returns blank [duplicate]

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);
};
}

Only attempt to login if the error code is 403

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;
}

How to called async function in useEffect after submiting in react native?

I would like once the user click on the submit button for the whole "page" to reload and the useEffect function to be called. The current behavior is that when the user clicks the submit button, the useEffect function is not called at all. It is as if it does not reload directly after submission. I don't know if this is due to async and await. I give you the code :
useEffect() :
useEffect(() => {
console.log('test useEffect');
(async () => {
try {
const value = await AsyncStorage.getItem('authToken_Lust');
if(value !== null) {
const decodedValue = jwt_decode(value);
const current_time = Date.now() / 1000;
if(decodedValue.exp < current_time) {
setMessage('Vous n\'êtes plus connecté(e).')
} else {
setMessage('Vous êtes connecté(e)');
}
}
} catch(err) {
console.log(err);
}
})();
}, []);
The code of the function called after the user clicks submit :
const onSubmit = async () => {
setLoading(true)
await fetch('http://192.168.1.36:3000/api/users/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: email,
password: password
})
})
.then((response) => response.json())
.then(async (res) => {
if(res.error) {
setMessage(res.error);
} else {
try {
await AsyncStorage.setItem('authToken_Lust', res.token);
} catch (err) {
console.log(err);
}
}
})
.catch((err) => console.log(err))
.finally(() => setLoading(false));
}
Tanks
Create a state variable to indicate that credentials have been successfully submitted (within this component).
const [submitted, setSubmitted] = useState(1);
Alter this state variable whenever you get a response from your api in onSubmit (to somehow update the component):
try {
await AsyncStorage.setItem('authToken_Lust', res.token);
setSubmitted(submitted+1);
}
Remove the dependency array from your useEffect altogether or change it to respond to submitted value changes;
useEffect(() => {
// ...
}, [submitted]);
you need to make these changes to your codes:
const onSubmit = () => {
setIsSubmitted(true);
setLoading(true);
fetch("http://192.168.1.36:3000/api/users/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
email: email,
password: password,
}),
})
.then((response) => response.json())
.then(async (res) => {
if (res.error) {
setMessage(res.error);
} else {
try {
await AsyncStorage.setItem("authToken_Lust", res.token);
} catch (err) {
console.log(err);
}
}
})
.catch((err) => console.log(err))
.finally(() => setLoading(false));
};
const asyncEffect = async () => {
try {
const value = await AsyncStorage.getItem("authToken_Lust");
if (value !== null) {
const decodedValue = jwt_decode(value);
const current_time = Date.now() / 1000;
if (decodedValue.exp < current_time) {
setMessage("Vous n'êtes plus connecté(e).");
} else {
setMessage("Vous êtes connecté(e)");
}
}
} catch (err) {
console.log(err);
}
};
useEffect(() => {
if (isSubmitted) {
console.log("test useEffect");
asyncEffect();
}
}, [isSubmitted]);
I changed all my code taking into account your advice that gives :
const [submitted, setSubmitted] = useState(1);
useEffect(() => {
console.log("test useEffect");
asyncEffect();
}, [submitted]);
const asyncEffect = async () => {
try {
const value = await AsyncStorage.getItem("authToken_Lust");
if (value !== null) {
const decodedValue = jwt_decode(value);
const current_time = Date.now() / 1000;
if (decodedValue.exp < current_time) {
setMessage("Vous n'êtes plus connecté(e).");
} else {
setMessage("Vous êtes connecté(e)");
}
}
} catch (err) {
console.log(err);
}
};
const onSubmit = () => {
setSubmitted(submitted+1);
setLoading(true);
fetch("http://192.168.1.36:3000/api/users/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
email: email,
password: password,
}),
})
.then((response) => response.json())
.then(async (res) => {
if (res.error) {
setMessage(res.error);
} else {
try {
await AsyncStorage.setItem("authToken_Lust", res.token);
} catch (err) {
console.log(err);
}
}
})
.catch((err) => console.log(err))
.finally(() => setLoading(false));
};
When I click on the submit button, the console log works fine (I get "test useEffect" on console) but the asyncAffect() function does not seem to be called.
The setMessage in asyncEffect () don't change at all.

javascript/react/redux async await refactoring

I would like to refactor the fetchRelationships function to use async await. I am not sure what the best way is to do it as this code contains nested .then at response.json().then((json) =>....
Could sby pls post the refactored version?
export const fetchRelationshipsError = (error) => {
return {
type: FETCH_RELATIONSHIPS_FAILURE,
payload: { error }
};
};
export const fetchRelationshipsRequest = () => {
return { type: FETCH_RELATIONSHIPS_REQUEST };
};
export const fetchRelationshipsSuccess = (relationships) => {
return {
type: FETCH_RELATIONSHIPS_SUCCESS,
payload: relationships
};
};
export const fetchRelationships = (url) => {
return (dispatch) => {
dispatch(fetchRelationshipsRequest());
fetch(url, config)
.then((response) => {
const responseObj = {
response: response,
payload: response.json().then((json) => {
return { body: json }
})
};
if (!response.ok) {
const error = new Error(response.statusText);
responseObj.payload.then((response) => {
show_error_alert(response.body);
dispatch(fetchRelationshipsError(response.body));
});
throw error;
}
return responseObj.payload;
})
.then((response) => {
dispatch(fetchRelationshipsSuccess(response.body))
})
.catch((error) => { console.log('Request failed', error); });
};
};
Solution:
export const fetchRelationships = (url) => {
return async (dispatch) => {
dispatch(fetchRelationshipsRequest());
try {
const response = await fetch(url, config);
const jsonResponse = await response.json();
if (!response.ok) {
show_error_alert(jsonResponse);
dispatch(fetchRelationshipsError(jsonResponse));
const error = new Error(response.statusText);
throw error;
}
dispatch(fetchRelationshipsSuccess(jsonResponse));
} catch(error) {
console.log('Request failed', error);
}
};
};
Ill take a stab at this:
export const fetchRelationshipsError = (error) => {
return {
type: FETCH_RELATIONSHIPS_FAILURE,
payload: { error }
};
};
export const fetchRelationshipsRequest = () => {
return { type: FETCH_RELATIONSHIPS_REQUEST };
};
export const fetchRelationshipsSuccess = (relationships) => {
return {
type: FETCH_RELATIONSHIPS_SUCCESS,
payload: relationships
};
};
export const fetchRelationships = (url) => {
return async (dispatch) => {
dispatch(fetchRelationshipsRequest());
try{
const response = await fetch(url, config)
const jsonResponse = await response.json()
if(!response.ok){
show_error_alert(jsonResponse);
dispatch(fetchRelationshipsError(jsonResponse));
const error = new Error(response.statusText);
throw error;
}
dispatch(fetchRelationshipsSuccess(jsonResponse));
}catch(error){
console.log('Request failed', error);
}
};

Categories