http post subscription and try catch - javascript

I use Angular2's http.post and sometimes the headers are not send to the CORS server. So I want to try till the request succeeds. But this code hangs in an endless loop?
var headers = new Headers();
headers.append('Content-Type', 'text/plain');
this.again = true;
while (this.again==true) {
http.post('https://localhost:44300/account/getip', "", { headers: headers })
.subscribe(
(res2) => {
try {
this.ip = res2.json();
this.ipstr = this.ip.replace(/\./g, '-');
this.again = false;
}
catch (err) {
console.error(err);
}
}
);
}

If you want to catch errors from request you could either:
Use the second callback of the subscribe method:
http.post('https://localhost:44300/account/getip', "", { headers: headers })
.subscribe(
(res2) => {
(...)
},
(error) => {
(...)
}
);
}
Use the catch operator:
http.post('https://localhost:44300/account/getip', "", { headers: headers })
.catch((error) => {
(...)
})
.subscribe(
(res2) => {
(...)
}
);
}
Regarding retrying requests, you could leverage the retry operator this way (with a timeout one):
this.http.get('https://localhost:44300/account/getip',
{ search: params })
.retryWhen(error => error.delay(500))
.timeout(2000, return new Error('delay exceeded'))
.map(res => res.json().postalCodes);
You can see the use of delay to wait for an amount of time before executing a new request...
This article could interest you as well:
https://jaxenter.com/reactive-programming-http-and-angular-2-124560.html

You can use the retry operator:
http.post('https://localhost:44300/account/getip', "", { headers: headers })
.retry(3)
.subscribe((res2) => {
this.ip = res2.json();
this.ipstr = this.ip.replace(/\./g, '-');
})

Related

Handling query in React and Express

Somewhere in my React application I used REST API to send request to the server. In my URL I want to use query (in the postIconsTransition method), but when I send a request to the server, server tells me could not found this URL (I build this error in my server). If I use this URL without any query the request in the postIconsTransition method works fine. postId and authContext.userId work fine, can anyone tell me what's wrong with my code?
In my component where I send request:
const likeHandler = async () => {
setLike(prevState => !prevState);
if (!like) {
try {
await postIconsTransition(props.postId, "inc");
} catch (error) {}
} else {
try {
await postIconsTransition(props.postId, "dec");
} catch (error) {}
}
};
In useHttp.js component:
const postIconsTransition = async (postId, addtionAddress) => {
return await transitionData(
`http://localhost:5000/post/${postId}/${authContext.userId}?t=${addtionAddress}`,
"POST",
null,
{ Authorization: `Bearer ${authContext.token}` }
);
};
transitionData method:
const transitionData = useCallback(
async (url, method = "GET", body = null, headers = {}) => {
setIsLoading(true);
const abortController = new AbortController();
activeHttpRequest.current.push(abortController);
try {
const response = await fetch(url, {
method,
body,
headers,
signal: abortController.signal
});
const responseData = await response.json();
activeHttpRequest.current = activeHttpRequest.current.filter(
reqCtrl => reqCtrl !== abortController
);
if (!response.ok) {
throw new Error(responseData.message);
}
setIsLoading(false);
return responseData;
} catch (error) {
modalContext.err(error);
setIsLoading(false);
throw error;
}
},
[modalContext.err]
);
In Express:
router.post(
"/:postId/:userId?t=inc",
tokenChecker,
postController.updateLikesComments
);
router.post(
"/:postId/:userId?t=dec",
tokenChecker,
postController.updateLikesComments
);
All of them work fine but when I use query in my URL, it's not working any more.
You don't specify query parameters in express routes like that. Just send them. Express can read it.
router.post(
"/:postId/:userId",
tokenChecker,
postController.updateLikesComments
);
// Notice that you don't need the other one.
and in your controller check the parameter
// controller's code
const t = req.query.t;
if (t === 'inc') {
// do what you want here
}
if (t === 'dec') {
// do what you want here
}

Angular HttpClient.put observable returning null

I subscribe to the Observable returned by my HttpClient.put(...), the response comes back null. Here's my code:
constructor(private http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
updateIsEnabled(type: string, isChecked: boolean) {
const url = `${this.isEnabledUrl}?type=${type}&isEnabled=${isChecked}`;
this.http.put(url, this.httpOptions)
.pipe(
catchError(this.handleError('updateIsEnabled'))
).subscribe(response => {
if (response.getStatusCode() == 200) {
//do something
}
});
}
But I get:
ERROR TypeError: Cannot read property 'getStatusCode' of null
I am sure that I'm responding with a 200 OK (Spring backend), but with an empty body. I figured maybe it was expecting a JSON. So I changed the options to:
httpTextOptions = {
headers: new HttpHeaders({ 'Content-Type': 'text/plain',
'Content-Length': '0'})
}
But still with the same results.
Any ideas?
Thanks
I don't get the point of catchError as it destroys the observable, not sure if it is a bug or I don't understand it properly but I have learned not to use it as it isn't useful for catching errors.
const { throwError } = rxjs;
const { catchError } = rxjs.operators;
throwError('error').pipe(
catchError(e => { console.log(e); })
).subscribe(val => { console.log(val); }, e => { console.log(e); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
You can get the error with a tap and an error handler that doesn't destroy the observable
const { throwError } = rxjs;
const { tap } = rxjs.operators;
throwError('error').pipe(
tap(undefined, e => { console.log(e); })
).subscribe(val => { console.log(val); }, e => { console.log(e); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>

Axios multiple request on interceptor

I'm using the library axios in my react app.
I'm having a problem with the interceptor.
My question is let say I have three requests happening concurrently and I don't have the token, the interceptor calling the getUserRandomToken three time, I want the interceptor will wait until I'm getting the token from the first request and then continue to the others.
P.S. the token he is with an expiration date so I also checking for it and if the expiration date is not valid I need to create a new token.
This is the interceptor:
axios.interceptors.request.use(
config => {
/*I'm getting the token from the local storage
If there is any add it to the header for each request*/
if (tokenExist()) {
config.headers.common["token"] = "...";
return config;
}
/*If there is no token i need to generate it
every time create a random token, this is a axios get request*/
getUserRandomToken()
.then(res => {
/*add the token to the header*/
config.headers.common["token"] = res;
return config;
})
.catch(err => {
console.log(err);
});
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
How about singleton object that will handle the token generations? something similar to this:
const tokenGenerator ={
getTokenPromise: null,
token: null,
getToken(){
if (!this.getTokenPromise){
this.getTokenPromise = new Promise(resolve=>{
/*supposed to be a http request*/
if (!this.token){
setTimeout(()=>{
this.token = 'generated';
resolve(this.token);
},0)
}else{
resolve(this.token);
}
})
}
return this.getTokenPromise;
}
you can reference this same object from the interceptors.
see example: JS FIddle
reference: reference
You can return a Promise from interceptor callback to "wait" until promise fullfiles (this will fit your case). Check out this example:
function axiosCall () {
return new Promise((resolve, reject) => {
Axios.post(URL, {apiKey}).then((response) => {
resolve(response.data.message);
}).catch((error) => {
reject(error);
});
});
}
instance.interceptors.request.use((config) => {
return axiosCall().then((tokenResponse) => {
setWebCreds(tokenResponse);
config.headers.Authorization = `Bearer ${tokenResponse}`;
return Promise.resolve(config)
}).catch(error => {
// decide what to do if you can't get your token
})
}, (error) => {
return Promise.reject(error);
});
More details here: https://github.com/axios/axios/issues/754
Following code doing certain tasks:
Update Token on 401
Make a queue of failed requests while the token is refreshing.
Restore the original request after token refreshing.
Once the peculiar request is given 200, remove it from the queue.
Config.js
import axios from 'axios';
import { AsyncStorage } from 'react-native';
import { stateFunctions } from '../../src/sharedcomponent/static';
const APIKit = axios.create({
baseURL: '',
timeout: 10000,
withCredentials: true,
});
const requestArray = [];
// Interceptor for Request
export const setClientToken = token => {
APIKit.interceptors.request.use(
async config => {
console.log('Interceptor calling');
let userToken = await AsyncStorage.getItem('userToken');
userToken = JSON.parse(userToken);
config.headers = {
'Authorization': `Bearer ${userToken}`,
'Accept': 'application/json',
"Content-Type": "application/json",
"Cache-Control": "no-cache",
}
// console.log('caling ' , config)
return config;
},
error => {
Promise.reject(error)
});
};
// Interceptor for Response
APIKit.interceptors.response.use(
function (response) {
if (requestArray.length != 0) {
requestArray.forEach(function (x, i) {
if (response.config.url == x.url) {
requestArray.splice(i, 1);
}
});
}
return response;
},
function (error) {
const originalRequest = error.config;
requestArray.push(originalRequest);
let reqData = "username=" + number + "&password=" + pin + "&grant_type=password" + "&AppType=2" + "&FcmToken=null";
// console.log('error ' , error);
if (error.message === "Request failed with status code 401" || error.statuscode === 401) {
if (!originalRequest._retry) {
originalRequest._retry = true;
return axios({
method: 'post',
url: '/api/login',
data: reqData,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache",
}
})
.then(res => {
let response = res.data;
console.log('successfull Login', response)
if (res.data.StatusCode == 200) {
AsyncStorage.setItem('userToken', JSON.stringify(response.access_token));
stateFunctions.UserId = response.UserId;
stateFunctions.CustomerContactID = response.CustomerContactID;
let obj = {
access_token: response.access_token,
token_type: response.token_type,
expires_in: response.expires_in,
UserId: response.UserId,
CustomerContactID: response.CustomerContactID,
Mobile: response.Mobile,
StatusCode: response.StatusCode
}
AsyncStorage.setItem('logindetail', JSON.stringify(obj));
if (requestArray.length != 0) {
requestArray.forEach(x => {
try {
console.log(x, "request Url");
x.headers.Authorization = `Bearer ${response.access_token}`;
x.headers["Content-Type"] = "application/x-www-form-urlencoded";
APIKit.defaults.headers.common["Authorization"] = `Bearer${response.access_token}`;
APIKit(x)
} catch (e) {
console.log(e)
}
});
}
return APIKit(originalRequest);
}
})
.catch(err => {
console.log(err);
});
}
}
return Promise.reject(error);
}
);
export default APIKit;
Home.js
gettingToken = async () => {
let userToken = await AsyncStorage.getItem('userToken');
userToken = JSON.parse(userToken);
await setClientToken(userToken);
}

Not working on cancellation of POST in Axios

The code is not working after the cancellation of POST in Axios. Can you tell me, what is wrong?
var CancelToken = axios.CancelToken;
var source = CancelToken.source();
axios
.post(
"http://api.image-service/upload",
data,
{
headers: { "Content-Type": "multipart/form-data" }
},
{
cancelToken: source.token
}
)
.then(response => {
if (response.data.StatusCode == 1) {
console.log("Retry", response.data);
} else if (response.data.StatusCode == 0) {
console.log("Success", response.data);
}
})
.catch(error => {
if (axios.isCancel(error)) {
console.log("Post Request canceled");
} else {
console.log("Error connecting to server", error);
}
});
source.cancel();
And one more question: can I do cancellation from other function? For example: if I wait more then 1 minute, I want to click the button for cancel request.
I faced the same issue with a get request.
You need to pass headers in axios properties.
axios.get(url, { cancelToken: ..., headers: { ... } }).then...

Async and localStorage not working properly

So I'm using React with React-Router.
I have a onEnter hook which checks if the user is authenticates yes/no and executes the desired action.
export function requireAuth(nextState, replaceState) {
if (!isAuthenticated()) {
if (!Storage.get('token')) replaceState(null, '/login');
return delegate().then(() => replaceState(null, nextState.location.pathname));
}
if (nextState.location.pathname !== nextState.location.pathname) {
return replaceState(null, nextState.location.pathname);
}
}
When the token is expired I call a delegate function which looks like:
export function delegate() {
const refreshToken = Storage.getJSON('token', 'refresh_token');
return getData(endpoint)
.then(res => {
Storage.set('token', JSON.stringify({
access_token: res.data.access_token,
refresh_token: refreshToken,
}));
});
}
The delegate function indeed refresh the tokens in the localStorage. But the requests after the replaceState are not using the updated token, but the previous one. I think this is a async issue, someone knows more about this?
Edit: The function where I use the token:
function callApi(method, endpoint, data) {
return new Promise((resolve, reject) => {
let headers = {
'Accept': 'application/json',
'X-API-Token': Storage.getJSON('token', 'access_token'),
};
const body = stringifyIfNeeded(data);
const options = { method, headers, body };
return fetch(endpoint, options)
.then(response =>
response.json().then(json => ({ json, response }))
).then(({ json, response }) => {
if (!response.ok) {
reject({ json, response });
}
resolve(json);
}).catch((error, response) => {
reject({ error, response });
});
});
}

Categories

Resources