无法在函数内再次调用 api
Can't call api again inside function
我的请求有问题。
这个想法是,如果请求失败,因为令牌过期功能将发送登录请求(使用会话数据中的数据)并将再次发送原始请求和 return 值。
我认为对此可能有明显的解决方案,但我几乎没有用 js 编写代码。
知道如何解决吗?
代码在这里:
apiCall(url, body, first = true) {
const httpOptions = {
headers: new HttpHeaders()
.set('Authorization', `Bearer ${sessionStorage.getItem('jwt')} `),
};
this.deleteNullParams(body);
return this.httpClient.post(this.API_URL + url, body, httpOptions).pipe(
map(response => {
if (response['result'] != 'success' && first) {
console.log("Failed");
this.loginWithSessionData();
return this.apiCall(url, body, false);
}
else {
return response;
}
})
);
}
getLocationDetails(body) {
return this.apiCall('/api/locationList', body);
}
loginWithSessionData() {
let password = sessionStorage.getItem('password');
let username = sessionStorage.getItem('username');
if (password && password != '' && username && username != '') {
const json = {};
json['username'] = username;
json['password'] = password;
this.userLogin(json).subscribe((res: any) => {
if (res['body']['result'] !== 'failed') {
sessionStorage.setItem('jwt', res.body['data'].token);
localStorage.setItem('login_data', JSON.stringify(res.body['data']));
return true;
}
}, (error) => { });
}
else {
sessionStorage.clear();
}
return false;
}
所以我想在获得有效令牌后再次 return apiCall,但由于某些原因,除登录之外的任何其他请求都不会在 apiCall 函数中发送。
编辑:
更具体地说,调用函数时会发生什么:
尝试获取数据,
由于没有(或过期)令牌而失败,
发送登录请求,获取,
设置令牌就是这样,
直到下一个 refresh/change 页才会发送更多请求。
回答
好的,接受的答案是正确的,只需添加 returns:
apiCall(url, body, first = true) {
const httpOptions = {
headers: new HttpHeaders()
.set('Authorization', `Bearer ${sessionStorage.getItem('jwt')}`)
};
this.deleteNullParams(body);
return this.httpClient.post(this.API_URL + url, body, httpOptions).pipe(
switchMap((response: any) => {
return iif(
() => response['result'] != 'success' && first,
this.loginWithSessionDataObs().pipe(switchMap(() => this.apiCall(url, body, false))),
of(response)
)
})
);
}
从可观察对象中获取值是异步的,这意味着您不能期望在订阅回调之后写一些语句,然后在回调之后执行。他们会运行更早。
在您的情况下,您需要使用多个 RxJS 函数(of
、iif
)和运算符(switchMap
)以使其相互依赖。
apiCall(url, body, first = true) {
const httpOptions = {
headers: new HttpHeaders()
.set('Authorization', `Bearer ${sessionStorage.getItem('jwt')}`)
};
this.deleteNullParams(body);
this.httpClient.post(this.API_URL + url, body, httpOptions);.pipe(
switchMap((response: any) => {
iif(
() => response['result'] != 'success' && first,
this.loginWithSessionData().pipe(switchMap(() => this.apiCall(url, body, false))),
of(response)
)
})
);
}
loginWithSessionData(): Observable<any> { // <-- return observable here
let username = sessionStorage.getItem('username');
let password = sessionStorage.getItem('password');
const storeData = (data: any) => {
sessionStorage.setItem('jwt', data.body['data'].token);
localStorage.setItem('login_data', JSON.stringify(data.body['data']));
}
return iif(
() => (password && password != '' && username && username != ''),
this.userLogin({ username: username, password: password }).pipe(
map((data: any) => {
storeData(data);
return true;
})
),
of(false).pipe(tap(() => sessionStorage.clear()))
);
}
然而,尽管它可能有效,但我想说可能有更好的方法来重新触发 HTTP 请求而不是递归调用。例如,您可以在最初不触发 HTTP 请求的情况下检查 token expiration。
我的请求有问题。
这个想法是,如果请求失败,因为令牌过期功能将发送登录请求(使用会话数据中的数据)并将再次发送原始请求和 return 值。
我认为对此可能有明显的解决方案,但我几乎没有用 js 编写代码。
知道如何解决吗?
代码在这里:
apiCall(url, body, first = true) {
const httpOptions = {
headers: new HttpHeaders()
.set('Authorization', `Bearer ${sessionStorage.getItem('jwt')} `),
};
this.deleteNullParams(body);
return this.httpClient.post(this.API_URL + url, body, httpOptions).pipe(
map(response => {
if (response['result'] != 'success' && first) {
console.log("Failed");
this.loginWithSessionData();
return this.apiCall(url, body, false);
}
else {
return response;
}
})
);
}
getLocationDetails(body) {
return this.apiCall('/api/locationList', body);
}
loginWithSessionData() {
let password = sessionStorage.getItem('password');
let username = sessionStorage.getItem('username');
if (password && password != '' && username && username != '') {
const json = {};
json['username'] = username;
json['password'] = password;
this.userLogin(json).subscribe((res: any) => {
if (res['body']['result'] !== 'failed') {
sessionStorage.setItem('jwt', res.body['data'].token);
localStorage.setItem('login_data', JSON.stringify(res.body['data']));
return true;
}
}, (error) => { });
}
else {
sessionStorage.clear();
}
return false;
}
所以我想在获得有效令牌后再次 return apiCall,但由于某些原因,除登录之外的任何其他请求都不会在 apiCall 函数中发送。
编辑:
更具体地说,调用函数时会发生什么:
尝试获取数据,
由于没有(或过期)令牌而失败,
发送登录请求,获取,
设置令牌就是这样,
直到下一个 refresh/change 页才会发送更多请求。
回答
好的,接受的答案是正确的,只需添加 returns:
apiCall(url, body, first = true) {
const httpOptions = {
headers: new HttpHeaders()
.set('Authorization', `Bearer ${sessionStorage.getItem('jwt')}`)
};
this.deleteNullParams(body);
return this.httpClient.post(this.API_URL + url, body, httpOptions).pipe(
switchMap((response: any) => {
return iif(
() => response['result'] != 'success' && first,
this.loginWithSessionDataObs().pipe(switchMap(() => this.apiCall(url, body, false))),
of(response)
)
})
);
}
从可观察对象中获取值是异步的,这意味着您不能期望在订阅回调之后写一些语句,然后在回调之后执行。他们会运行更早。
在您的情况下,您需要使用多个 RxJS 函数(of
、iif
)和运算符(switchMap
)以使其相互依赖。
apiCall(url, body, first = true) {
const httpOptions = {
headers: new HttpHeaders()
.set('Authorization', `Bearer ${sessionStorage.getItem('jwt')}`)
};
this.deleteNullParams(body);
this.httpClient.post(this.API_URL + url, body, httpOptions);.pipe(
switchMap((response: any) => {
iif(
() => response['result'] != 'success' && first,
this.loginWithSessionData().pipe(switchMap(() => this.apiCall(url, body, false))),
of(response)
)
})
);
}
loginWithSessionData(): Observable<any> { // <-- return observable here
let username = sessionStorage.getItem('username');
let password = sessionStorage.getItem('password');
const storeData = (data: any) => {
sessionStorage.setItem('jwt', data.body['data'].token);
localStorage.setItem('login_data', JSON.stringify(data.body['data']));
}
return iif(
() => (password && password != '' && username && username != ''),
this.userLogin({ username: username, password: password }).pipe(
map((data: any) => {
storeData(data);
return true;
})
),
of(false).pipe(tap(() => sessionStorage.clear()))
);
}
然而,尽管它可能有效,但我想说可能有更好的方法来重新触发 HTTP 请求而不是递归调用。例如,您可以在最初不触发 HTTP 请求的情况下检查 token expiration。