I am trying to make simple login authentication using my REST api made using passport module in nodejs .
My frontend calls this function but this function returns an error and authentication does not occur . The post fn is not working .
export const loginUser = (creds) => (dispatch) => {
// We dispatch requestLogin to kickoff the call to the API
dispatch(requestLogin(creds))
return fetch(baseUrl + 'users/login', {
method: 'POST',
headers: {
'Content-Type':'application/json'
},
body: JSON.stringify(creds)
})
.then(response => {
console.log("HELLO");
if (response.ok) {
return response;
} else {
var error = new Error('Error ' + response.status + ': ' + response.statusText);
error.response = response;
throw error;
}
},
error => {
throw error;
})
.then(response => response.json())
.then(response => {
console.log("HELLO2");
if (response.success) {
// If login was successful, set the token in local storage
localStorage.setItem('token', response.token);
localStorage.setItem('creds', JSON.stringify(creds));
console.log(response);
// Dispatch the success action
// dispatch(fetchFavorites());
dispatch(receiveLogin(response));
}
else {
var error = new Error('Error ' + response.status);
error.response = response;
throw error;
}
})
.catch(error => dispatch(loginError(error.message)))
};
So, the reason behind this was that my app was hosting static files as well as server together .
Thus, the server was responding with 404 file instead of json data as response .
Resolved when i partitioned the client and server separately and hosted them on different platforms .
Related
I have an API get request that does a search in a database and displays the information in a table. Currently I have a componentDidMount() that ruins a GET request and sets candidate with an array of data. When I run searchUpdate this will change the state candidate with data the searched data. The function successfully changes and updates the table with the searched information. The issue I am having trouble with is when the search doesn't find anything it sets candidate to {}, resulting in an error TypeError: this.state.candidate.map is not a function. Is there a way that I can work around this?
Code:
componentDidMount() {
const access_token = JSON.parse(localStorage["appState"]).user.access_token;
console.log("Access token = " + access_token);
const config = {
headers: {
Authorization: "Bearer " + access_token,
},
};
axios
.get(
"https://api/v1/candidate/list/lead/new lead?page=1",
config
)
.then((response) => {
console.log(response.data);
this.setState({ candidate: response.data.data });
})
.catch((error) => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log("Error", error.message);
}
console.log(error.config);
});
}
searchUpdate = (search) => {
const access_token = JSON.parse(localStorage["appState"]).user.access_token;
// console.log('Access token = ' + access_token);
// Hard code token for the time being
this.setState({ ...this.state, search: search });
console.log(search);
const config = {
headers: {
Authorization: "Bearer " + access_token,
},
};
axios
.get(
"https://api/v1/candidate/search?q=" + search,
config
)
.then((response) => {
console.log(response.data);
this.setState({ candidate: response.data });
})
.catch((error) => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log("Error", error.message);
}
console.log(error.config);
});
};
render() {
if (this.state.candidate_counts === undefined) {
return null;
}
const dataTable = this.state.candidate.map((item) => {
return {
id: item.id,
name: item.name,
current_company_name: item.current_company_name,
job: item.job_title,
owner: item.owner.name,
updated: new Date(item.updated_at).toLocaleString(),
email: item.email,
phone: item.phone,
is_active: item.is_active,
is_snoozed: item.is_snoozed,
};
});
return (
<div className="candidate-list-container">
<DataTable
theme="solarized"
customStyles={customStyles}
noHeader={true}
fixedHeader={true}
columns={columns}
data={dataTable}
onRowClicked={this.handleChange}
pagination={false}
responsive={true}
/>
</div>
I'd strongly recommend making sure that the API has a consistent response type. This means that if /v1/candidates?age=20 returns an array of Candidates with age 20, then the API should always respond with an array.
If you can't control the API's design, then you can duck-type:
if (this.state.candidate.map) {
// you know you can (probably) use .map()
}
Another case is that you could check if candidate is {}. You can't do this.state.candidate === {} because this compares the reference. This is how you should check for an empty object specifically: https://stackoverflow.com/a/32108184/1231844.
I have a react native app which has being working well on many devises, by recently I am getting "Catch Error JSON Parse Error: Unrecognised token'<'" on some devises, both Android and IOS.
See Code
fetch(url, {method: 'POST', headers: {Authorization : `Bearer ${user.token}`}})
.then(response => {
const statusCode = response.status;
const responseJson = response.json();
return Promise.all([statusCode, responseJson]);
})
.then(res => {
const statusCode = res[0];
const responseJson = res[1];
// console.log(responseJson);
if (statusCode == 200) {
if (responseJson.reload.status != 1 ) {
Alert.alert(I18n.t('phrases.transaction_failed'), I18n.t('phrases.transaction_not_completed') + ' - ' + responseJson.transaction.status_desc)
}else{
Actions.success({data: responseJson});
}
}else if(statusCode == 422){
alert('missing fields');
console.warn(responseJson);
}else{
Alert.alert(I18n.t('words.error'), responseJson.message);
console.log(res);
}
})
.catch(err => {
alert('Catch Error ' + err.message);
console.log(err);
}).finally( fin => this.setState({ loading: false }) )
}
The problem is probably that in some situations, the endpoint you're calling isn't returning JSON, it's returning HTML, probably an error page accompanying an HTTP error. So you'll need to check for that in your app and diagnose why it's happening in some situations and not in others.
So why are you trying to parse HTML as JSON? You haven't shown us any code, but my guess is that you've run into the footgun in the fetch API and have code like this:
fetch("/your/endpoint")
.then(response => response.json()) // <=== This is the footgun
.then(data => {
// ...use the data...
})
.catch(error => {
// ...report the error...
});
The problem is that fetch doesn't reject the promise on HTTP errors, only network errors. You need to check that you didn't get an HTTP 500, 404, etc. status code:
fetch("/your/endpoint")
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then(data => {
// ...use the data...
})
.catch(error => {
// ...report the error...
});
i wrote an axios interceptor for refresh token in vue js (main.js) and i want when i get 401 error status code in my project it try for getting new token with my refresh_token and send request again with new token
if the refresh token is not valid too i want to redirect to login page and break the axios requests
but my problem is when refresh token is not valid i will stuck in loop of refresh token api call and i will get multiple error (duplicate navigation) in console
another problem is i cant catch error in catch block and always it goes to then block and response is undefined
axios.interceptors.response.use((response) => {
return response
},
function (error) {
const originalRequest = error.config;
if (error.response.status === 401) {
axios.post(process.env.VUE_APP_BASE_URL + process.env.VUE_APP_REFRESH_TOKEN,
{
"refresh_token": localStorage.getItem("refresh_token")
})
.then(res => {
localStorage.setItem("token", "Bearer " + res.data.result.access_token);
originalRequest.headers['Authorization'] = localStorage.getItem("token");
return axios(originalRequest);
}).catch(error=>{
console.log(error);
router.push({'name':'login'})
})
}
});
You can try to add a flag. For example,
let isAlready401=false
axios.interceptors.response.use((response) => {
return response
},
function (error) {
const originalRequest = error.config;
if (!isAlready401&&error.response.status === 401) {
isAlready401 = true
axios.post(process.env.VUE_APP_BASE_URL + process.env.VUE_APP_REFRESH_TOKEN,
{
"refresh_token": localStorage.getItem("refresh_token")
})
.then(res => {
localStorage.setItem("token", "Bearer " + res.data.result.access_token);
originalRequest.headers['Authorization'] = localStorage.getItem("token");
return axios(originalRequest);
}).catch(error=>{
console.log(error);
router.push({'name':'login'})
isAlready401 = false
})
}
});
I am trying to make a generic Fetch method for my React project. The plan is to pass my fetch method a url, and some config data (method, header etc). And then return less technical data to my calling methods. I'd like to return the data from the api call, and the payload, which is json data.
return fetch(URL, config)
.then((response) => {
console.log('Fetch - Got response: ', response);
return response;
})
.then((response) => {
console.log('Json: ', response.status);
const result = { data: response.json(), status: response.status };
return result;
})
.catch((e) => {
console.log(`An error has occured while calling the API. ${e}`);
reject(e);
});
This is my initial attempt, but I'm not quite sure what I'm doing.
my console log that logs 'response', contains the response from the API call:
body: (...)
bodyUsed: true
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://localhost:49487//api/xxx/yyy"
So the API call completes, and I get a 200 status.
The line:
console.log('Json: ', response.status);
return 200 as expected.
What I have been doing before is
return response.json()
And then my calling class gets the paylad, but no status. What I am trying to do is return my payload, AND the status.
So I attempted to change it to this:
const result = { data: response.json(), status: response.status };
return result;
But my calling app now sees:
data: Promise {<resolved>: Array(9)}
status: 200
I was expecting to get data: MyPayloadArray, status: 200
I think I'm misunderstanding promises here. (I'm quite green with them).
My data accessor that uses my Fetch method:
static GetAll() {
return new Promise((resolve, reject) => {
const request = {
method: 'GET',
URL: `${Server.ApiURL}/api/admin/clients`,
};
fetchData(request)
.then((result) => {
console.log('GetAll sees response as ', result);
resolve(result);
})
.catch((error) => {
reject(new Error(error));
});
});
}
I'm trying to call my data accessor class, like this:
componentDidMount() {
ClientDataAccessor.GetAll()
.then((response) => {
console.log('Got list!', response);
this.setState({ clients: response, isLoading: false });
})
.catch((error) => {
console.log('Got error on list screen', error);
});
}
How can I get just the status, and the payload back to my DataAccesor class? I think I'm just screwing up the Promises... But not sure of the best pattern here.
I'm going UI class, onComponentDidMount -> DataAccessor.GetAll -> FetchData. I think I'm abusing the Promise somewhere.
The issue here is that response.json() returns another promise. You would have to resolve that promise object yourself and return the object you are looking for.
return fetch(URL, config)
.then((response) => {
console.log('Fetch - Got response: ', response);
return response;
})
.then((response) =>
response.json()
.then( (data) => { data, status: response.status } )
)
.catch((e) => {
console.log(`An error has occured while calling the API. ${e}`);
reject(e);
});
Very ugly...
Why not move it to async/await funtion? All browsers support it at this stage...
async myFetch(URL, config) {
try {
const response = await fetch(URL, config);
console.log('Fetch - Got response:', response);
const data = await response.json();
console.log('Fetch - Got data:', data);
return { data, status: response.status }
}
catch (e) {
console.error(`An error has occured while calling the API. ${e}`);
throw e;
}
}
Just be aware, in both cases, that your function returns another promise.
I think you can solve your issue using Promise.resolve, chaining promise to obtain your result object:
...
.then(response => {
var status = response.status;
return Promise.resolve(response.json())
.then(data => ({ data, status }))
})
The Promise.resolve can take a Promise as parameter and return a promise that will flatten the chain, so you can get the value of the json parse and work with it.
You need to further resolve res.json() to get the info. You can do something like this:
return fetch(URL, config)
.then((response) => {
console.log('Fetch - Got response: ', response);
return response;
})
.then(response =>
response.json().then(json => ({
status: response.status,
json
})
))
.then(({ status, json }) => {
console.log({ status, json });
return { data: json, status: status };
})
.catch((e) => {
console.log(`An error has occured while calling the API. ${e}`);
reject(e);
});
The neat version of above could be:
return fetch(URL, config)
.then(response => response.json()
.then(json => ({ status: response.status, data: json }) )
)
Note: Removed the reject(e) from inside of catch, because it is redundant.
I'm implementing token authentication. My access token expires every N minutes and then a refresh token is used to log in and get a new access token.
I use Axios for my API calls. I have an interceptor set up to intercept 401 responses:
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => { setTokens(success.access_token, success.refresh_token) },
error => { console.log('Refresh login error: ', error) }
)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken()
return axios(err.config);
}
throw err
})
Basically, as I intercept a 401 response, I want to do a login and then retry the original rejected request with the new tokens. My serviceRefreshLogin function calls setAccessToken() in its then block. But the problem is that
the then block happens later than the getAccessToken() in the interceptor, so the retry happens with the old expired credentials.
getAccessToken() and getRefreshToken() simply return the existing tokens stored in the browser (they manage localStorage, cookies, etc).
How would I go about ensuring statements do not execute until a promise returns?
(Here's a corresponding issue on Github: https://github.com/mzabriskie/axios/issues/266)
Just use another promise :D
axios.interceptors.response.use(undefined, function (err) {
return new Promise(function (resolve, reject) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => {
setTokens(success.access_token, success.refresh_token)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
axios(err.config).then(resolve, reject);
},
error => {
console.log('Refresh login error: ', error);
reject(error);
}
);
}
throw err;
});
});
If your enviroment doesn't suport promises use polyfill, for example https://github.com/stefanpenner/es6-promise
But, it may be better to rewrite getRefreshToken to return promise and then make code simpler
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
return getRefreshToken()
.then(function (success) {
setTokens(success.access_token, success.refresh_token) ;
err.config.__isRetryRequest = true;
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
return axios(err.config);
})
.catch(function (error) {
console.log('Refresh login error: ', error);
throw error;
});
}
throw err;
});
Demo https://plnkr.co/edit/0ZLpc8jgKI18w4c0f905?p=preview
Could do it in the request instead of the response, and it'd probably be cleaner since it'd avoid hitting the server when the access token's expired. Copying from this article:
function issueToken() {
return new Promise((resolve, reject) => {
return client({
...
}).then((response) => {
resolve(response);
}).catch((err) => {
reject(err);
});
});
}
client.interceptors.request.use((config) => {
let originalRequest = config;
if (tokenIsExpired && path_is_not_login) {
return issueToken().then((token) => {
originalRequest['Authorization'] = 'Bearer ' + token;
return Promise.resolve(originalRequest);
});
}
return config;
}, (err) => {
return Promise.reject(err);
});