This question already has answers here:
What's the difference between returning value or Promise.resolve from then()
(6 answers)
Closed 4 years ago.
I have questions about promises. I'm just starting to deal with them and it's not that easy to understand!
I'm trying to setup an authentication system for my app.
RegisterPage
handleSubmit looks like that:
handleSubmit(event) {
event.preventDefault();
const { user } = this.state;
//some code here
userActions.register(user);
}
UserActions
function register(user) {
userService.register(user)
.then(
user => {
success(user);
},
error => {
failure(error.toString());
}
);
function success(user) { return { type: "REGISTER_SUCCESS", user } }
function failure(error) { return { type: "REGISTER_ERROR", error } }
}
UserService
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`/api/users/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}
Question 1. That code is "working" but not like I want. That way, even if the request success, I still can have error from the server, like duplicate username or something like that. I guess what I want is to return Promise.reject() not just if !response.ok but also if I have errors in the JSON returned right?
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
else if(data.errors) {
return Promise.reject(data.message);
}
return data;
});
}
Question 2. If everything's fine, should I return data or return Promise.resolve(data)? And why?
Checkout the documentation for fetch here: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
It seems you should be using .catch() to get server errors and just use throw new Error() for having errors.
You don't really need to use Promise.resolve or Promise.reject.
To help refactor what you have, you can try this
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`/api/users/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text()
.then(text => {
if (!response.ok) {
const error = (data && data.message) || response.statusText;
throw new Error(error);
} else {
const data = text && JSON.parse(text);
return data;
}
})
.catch(error => throw new Error(err));
}
Related
I'm new in JavaScript and i have a task to post an email input from form to a node server,everything works fine,but i should implement this functionality:
When an email is forbidden#gmail.com, the server responds with the 422 status code and payload which contains the information about the error. Use browser developer tools to examine the response for this scenario. Display the error message in the browser using window.alert().
I created a customException,it gives me the error in the console,but the server still responds with the 200 status code,but as i understand,it should give an error and the post should not work.How to do this task,i have no idea..?
Fetch functions:
import { validateEmail } from './email-validator.js'
export const sendSubscribe = (emailInput) => {
const isValidEmail = validateEmail(emailInput)
if (isValidEmail === true) {
sendData(emailInput);
// if (emailInput === 'forbidden#gmail.com'){
// throw new CustomException('422');
// }
}
}
const sendHttpRequest = (method, url, data) => {
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data ? {
'Content-Type': 'application/json'
} : {}
}).then(response => {
if (response.status >= 400) {
return response.json().then(errResData => {
const error = new Error('Something went wrong!');
error.data = errResData;
throw error;
});
}
return response.json();
});
};
const sendData = (emailInput) => {
sendHttpRequest('POST', 'http://localhost:8080/subscribe', {
email: emailInput
}).then(responseData => {
console.log(responseData);
}).catch(err => {
console.log(err, err.data);
});
}
function CustomException(message) {
const error = new Error(message);
error.code = "422";
window.alert('Forbidden email,please change it!')
return error;
}
CustomException.prototype = Object.create(Error.prototype);
Validate function:
const VALID_EMAIL_ENDINGS = ['gmail.com', 'outlook.com', 'yandex.ru']
export const validateEmail = (email) => !!VALID_EMAIL_ENDINGS.some(v => email.includes(v))
export { VALID_EMAIL_ENDINGS as validEnding }
Please help.Thanks in advance!
Something like this should work:
Server code:
Simplify validate function.
export const isValid = (email) => {
if (email === 'forbidden#gmail.com') {
return false
}
return true
}
Then on your route, something like this, assuming expressjs behind.
app.post('/subscribe', (req, res, next) => {
const email = req.body.email
if (!isValid(email)) {
return res.status(433).send('Email is forbidden')
}
return res.status(200).send('Success')
})
In your frontend you can just post to /subscribe with the email payload
const sendHttpRequest = (method, url, data) => {
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data ? {
'Content-Type': 'application/json'
} : {}
})
.then(response => response.json())
};
And in your sendData you can catch the error, like you're doing
const sendData = (emailInput) => {
sendHttpRequest('POST', 'http://localhost:8080/subscribe', {
email: emailInput
}).then(responseData => {
console.log(responseData);
}).catch(err => {
console.log(err); // Email is forbidden
window.alert('Boo!')
});
}
Sidenote: In most cases prototyping should be avoided in javascript.
The following asynchronous function returns 2 callbacks, so I did what I usually do to return the response
Added return before the fetch
Added return before the result (json.access_token)
but this time console.log(httpResponse, 'fetch json') is undefined on the console and console.log(json.access_token) return the right value.
What do I need to change?
From client
GetJSON(NewURLCode).then(httpResponse => {
console.log(httpResponse, 'fetch json')
}
From Server
GetJSON(NewURLCode){
return fetch("https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body': data
}).then((httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
} else {
return Promise.reject("Fetch did not succeed");
}
}).then((json) => {
console.log(json.access_token)
return json.access_token
}).catch(err => console.log(err));
}
Unlike the previous function, the following function has only one promise and returns the right object from the client
client
insert_coll ('Token',toInsert).then((ins_result) => {consule.log(ins_result)}
Backend
insert_coll{
return wixData.update(myCollection, toUpdate, options)
.then( ( results) => {
let item = results; //see item below
return results
} )
.catch( (err) => {
let errorMsg = err;
} );
}
}
The error was not accrued because of the return statement, I added a return before httpResponse.json();
It caused because the WIX onReady component called twice...
So for solving this issue I used the following WIX solution :
$w.onReady(function () {
if (wixWindow.rendering.env === "browser") {
//your code
}})
You can return that fetch itself, and handle those stuffs in client.
Like,
Server
GetJSON(NewURLCode){
return fetch("https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body': data
});
}
Client
GetJSON(NewURLCode).then(httpResponse => {
if (httpResponse.ok) {
httpResponse.json();
} else {
Promise.reject("Fetch did not succeed");
}
}).then((json) => {
console.log(json.access_token)
return json.access_token
}).catch(err => console.log(err));
}
Otherwise, just return a Promise after those are resolved in the server. Like,
Server
async GetJSON(NewURLCode){
return new Promise(async (resolve, reject) => {
try {
const httpResponse= await fetch("https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body': data
});
if (httpResponse.ok) {
const json = await httpResponse.json();
resolve(res.access_token);
} else {
reject("Fetch did not succeed");
}
} catch(err) {
reject(err);
}
});
}
Client
GetJSON(NewURLCode).then(httpResponse => {
console.log(httpResponse, 'fetch json')
}).catch(err => console.log(err));
This question already has answers here:
Chained promises not passing on rejection
(4 answers)
Closed 3 years ago.
I want to return a fetch promise to upper layer to use, but I found even this fetch promise fail (be catched), upper layer's then still be called. Is it possible "upper layer's then" only be called when fetch success?
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return data;
})
.catch(err => {
message.error(err.toString() || "please retry");
return err;
});
}
// then I call request this way:
export function userLogin(account) {
return request(`${domain}/signin/get_accesstoken`, {
method: "POST"
}).then(data => {
// even catch be called, this then still be called. Is it possible only call this then when catch not be called ?
do something;
return data;
});
}
Second edit:
I try to return a promise in then, but look like it is not a promise be returned, I don't know why.
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return new Promise(resolve=>{
resolve(data)
});
})
.catch(err => {
message.error(err.toString() || "please retry");
return new Promise((resolve, reject)=>{
reject(err)
});
});
}
Edit third:
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return data;
})
.catch(err => {
message.error(err.toString() || "please retry");
return;
});
}
// then I call request this way:
export function userLogin(account) {
return request(`${domain}/signin/get_accesstoken`, {
method: "POST"
}).then(data => {
// add this
if (!data) {
return
}
do something;
return data;
});
}
if you want to call your upper layer's then only in case of success then throw some error in catch block of fetch instead of returning err.
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return data;
})
.catch(err => {
message.error(err.toString() || "please retry");
throw new Error('fetch failed'); // throw error
});
}
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);
};
}
I have a yale smart alarm and come across the the below javascript that allows you to access the alarm to get the status and set it. I'm wanting to use this in my home assistant set to which uses python.
const fetch = require('node-fetch');
const setCookie = require('set-cookie-parser');
const urls = {
login: 'https://www.yalehomesystem.co.uk/homeportal/api/login/check_login',
getStatus: 'https://www.yalehomesystem.co.uk/homeportal/api/panel/get_panel_mode',
setStatus: 'https://www.yalehomesystem.co.uk/homeportal/api/panel/set_panel_mode?area=1&mode=',
};
function getSessionCookie(username, password) {
let sessionCookie = null;
return fetch(urls.login, {
method: 'POST',
body: `id=${encodeURIComponent(username)}&password=${password}&rememberme=on¬ify_id=®_id=Name`,
headers: {
'Accept': 'application/json, application/xml, text/plain, text/html, *.*',
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
},
})
.then((res) => {
sessionCookie = res.headers._headers['set-cookie'];
return res.json();
}).then(json => {
if (json.result === '0') {
return Promise.reject('Incorrect account details');
}
else {
return sessionCookie[0];
}
})
}
function getStatus(sessionCookie) {
return fetch(urls.getStatus, {
method: 'POST',
headers: {
'Cookie': sessionCookie,
},
}).then(res => res.text()).then(textResponse => {
// When initially writing this code I found if cookie payload
// was invalid I got this text response so I added this code to
// handle this, shouldn't happen but good to have an error message
// for this use case
if (textResponse === 'Disallowed Key Characters.') {
return Promise.reject('Invalid request');
}
else {
try {
// Hopefully if we got to this point we can parse the json
const json = JSON.parse(textResponse);
if (json.result === '0') {
return Promise.reject('Unable to get status');
}
else {
return json;
}
} catch (error) {
// If you get this error message I likely have not handled
// a error state that I wasnt aware of
return Promise.reject('Unable to parse response');
}
}
});
}
function setStatus (sessionCookie, mode) {
return new Promise((resolve, reject) => {
if (!sessionCookie || sessionCookie.length === 0) {
reject('Please call getSessionCookie to get your session cookie first');
}
if (mode !== 'arm' && mode !== 'home' && mode !== 'disarm') {
reject('Invalid mode passed to setStatus');
}
resolve(fetch(`${urls.setStatus}${mode}`, {
method: 'POST',
headers: {
'Cookie': sessionCookie,
},
}));
});
}
module.exports = {
getSessionCookie,
getStatus,
setStatus,
}
i'm every new to coding but was able to piece the below together to return the current status of my alarm. the problem is I'm unable to get it to work. based on the above code could someone please tell me what I'm missing, or if I'm going down the wrong rabbit hole....
import requests
import webbrowser
url = “https://www.yalehomesystem.co.uk/homeportal/api/login/check_login”
payload = {‘username’: ‘email#domaim.com’, ‘password’: ‘mypass’}
with requests.session() as s:
# fetch the login page
s.get(url, data=payload)
url1='https://www.yalehomesystem.co.uk/homeportal/api/panel/get_panel_mode'
# post to the login form
r = s.post(url1, data=payload)
print(r.text)
To add more contexts I'm getting the following error
{"result":"0","message":"system.permission_denied","code":"999"}