angular 自定义 http 请求前 2 个刷新令牌
angular 2 refresh token before custom http request
如果令牌已过期,我想在自定义 http 请求之前刷新令牌。当我确定令牌已过期但它给出以下控制台结果时,我尝试了我的代码:
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
............ lots of the same sentences and finally exception:
EXCEPTION: Uncaught (in promise): Error: Error in :0:0 caused by: too much recursion
k@http://localhost/xxx/node_modules/zone.js/dist/zone.min.js:1:11750
............
据我所知,在刷新令牌期间它会进入无限循环。我已经在其他地方用一个按钮测试了 updateToken() 方法,它工作正常。
我做错了什么?
自定义 http 服务
import { Injectable } from '@angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http';
import { tokenNotExpired } from "angular2-jwt";
import { Observable } from "rxjs/Observable";
@Injectable()
export class HttpService extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = localStorage.getItem('access_token'); // your custom token getter function here
options.headers.set('Authorization', `Bearer ${token}`);
super(backend, options);
}
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('access_token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = {headers: new Headers()};
}
options.headers.set('Authorization', `Bearer ${token}`);
} else { // we have to add the token to the url object
url.headers.set('Authorization', `Bearer ${token}`);
}
console.log("tokenNotExpired?: " + tokenNotExpired('access_token'));
if(tokenNotExpired('access_token')){ // if token is NOT expired
return super.request(url, options).catch(this.catchAuthError(this));
}else{ // if token is expired
console.log("Token refresh is required");
return this.updateToken()
.flatMap((result: boolean) => {
console.log("updateToken result");
console.log(result);
if (result) {
return super.request(url, options).catch(this.catchAuthError(this));
} else {
return Observable.throw(new Error('Can\'t refresh the token'));
}
});
}
}
updateToken(): Observable<any> {
console.log("updateToken() method inside");
let body: string = 'refresh_token=' + localStorage.getItem('refresh_token') + '&client_id=' + localStorage.getItem('resource') + '&grant_type=refresh_token';
return super.post(
localStorage.getItem("authUrl"),
body,
new RequestOptions({headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' })})
)
.map((response: Response) => {
let returnedBody: any = response.json();
console.log("post returnedBody");
console.log(returnedBody);
if (typeof returnedBody.access_token !== 'undefined'){
localStorage.setItem('access_token', returnedBody.access_token);
localStorage.setItem('refresh_token', returnedBody.refresh_token);
console.log("Token Refreshed: refreshed access_token");
console.log(localStorage.getItem('access_token'));
return true;
}
else {
return false;
}
});
}
private catchAuthError (self: HttpService) {
return (res: Response) => {
console.log(res);
return Observable.throw(res);
};
}
}
应用模块
@NgModule({
imports: [ .......... ],
declarations: [ ....... ],
providers: [
{
provide: HttpService,
useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new HttpService(backend, options);
},
deps: [XHRBackend, RequestOptions]
}
],
bootstrap: [ Application ]
})
在您的 updateToken
方法中,您正在调用 super.post
,这相当于 Http.prototype.post.apply(this...)
,并且 super.post
将在内部调用 this.request()
.
this
上下文是您的自定义 HttpService
,它以 HttpService
request
方法的递归调用结束。您应该改为调用 super.request
:
return super.request(
new Request({
method: RequestMethod.Post,
url: localStorage.getItem("authUrl"),
body,
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
})
)
.map((response: Response) => {
let returnedBody: any = response.json();
console.log("post returnedBody");
console.log(returnedBody);
if (typeof returnedBody.access_token !== 'undefined') {
localStorage.setItem('access_token', returnedBody.access_token);
localStorage.setItem('refresh_token', returnedBody.refresh_token);
console.log("Token Refreshed: refreshed access_token");
console.log(localStorage.getItem('access_token'));
return true;
} else {
return false;
}
});
另请注意,创建自定义 Http 服务可能不是最佳主意。
但也许您可以创建一个可以注入 http 的服务,因为您可能不需要通过身份验证就可以从 ajax 调用中获取一些简单的静态数据。
这也可以避免您遇到的超出递归调用堆栈的问题。
@Injectable()
export class MyAuhtHttpService{
constructor(private http:Http){}
}
如果令牌已过期,我想在自定义 http 请求之前刷新令牌。当我确定令牌已过期但它给出以下控制台结果时,我尝试了我的代码:
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
Token refresh is required app.js:1:92855
updateToken() method inside app.js:1:93301
tokenNotExpired?: false app.js:1:92674
............ lots of the same sentences and finally exception:
EXCEPTION: Uncaught (in promise): Error: Error in :0:0 caused by: too much recursion
k@http://localhost/xxx/node_modules/zone.js/dist/zone.min.js:1:11750
............
据我所知,在刷新令牌期间它会进入无限循环。我已经在其他地方用一个按钮测试了 updateToken() 方法,它工作正常。
我做错了什么?
自定义 http 服务
import { Injectable } from '@angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http';
import { tokenNotExpired } from "angular2-jwt";
import { Observable } from "rxjs/Observable";
@Injectable()
export class HttpService extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = localStorage.getItem('access_token'); // your custom token getter function here
options.headers.set('Authorization', `Bearer ${token}`);
super(backend, options);
}
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('access_token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = {headers: new Headers()};
}
options.headers.set('Authorization', `Bearer ${token}`);
} else { // we have to add the token to the url object
url.headers.set('Authorization', `Bearer ${token}`);
}
console.log("tokenNotExpired?: " + tokenNotExpired('access_token'));
if(tokenNotExpired('access_token')){ // if token is NOT expired
return super.request(url, options).catch(this.catchAuthError(this));
}else{ // if token is expired
console.log("Token refresh is required");
return this.updateToken()
.flatMap((result: boolean) => {
console.log("updateToken result");
console.log(result);
if (result) {
return super.request(url, options).catch(this.catchAuthError(this));
} else {
return Observable.throw(new Error('Can\'t refresh the token'));
}
});
}
}
updateToken(): Observable<any> {
console.log("updateToken() method inside");
let body: string = 'refresh_token=' + localStorage.getItem('refresh_token') + '&client_id=' + localStorage.getItem('resource') + '&grant_type=refresh_token';
return super.post(
localStorage.getItem("authUrl"),
body,
new RequestOptions({headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' })})
)
.map((response: Response) => {
let returnedBody: any = response.json();
console.log("post returnedBody");
console.log(returnedBody);
if (typeof returnedBody.access_token !== 'undefined'){
localStorage.setItem('access_token', returnedBody.access_token);
localStorage.setItem('refresh_token', returnedBody.refresh_token);
console.log("Token Refreshed: refreshed access_token");
console.log(localStorage.getItem('access_token'));
return true;
}
else {
return false;
}
});
}
private catchAuthError (self: HttpService) {
return (res: Response) => {
console.log(res);
return Observable.throw(res);
};
}
}
应用模块
@NgModule({
imports: [ .......... ],
declarations: [ ....... ],
providers: [
{
provide: HttpService,
useFactory: (backend: XHRBackend, options: RequestOptions) => {
return new HttpService(backend, options);
},
deps: [XHRBackend, RequestOptions]
}
],
bootstrap: [ Application ]
})
在您的 updateToken
方法中,您正在调用 super.post
,这相当于 Http.prototype.post.apply(this...)
,并且 super.post
将在内部调用 this.request()
.
this
上下文是您的自定义 HttpService
,它以 HttpService
request
方法的递归调用结束。您应该改为调用 super.request
:
return super.request(
new Request({
method: RequestMethod.Post,
url: localStorage.getItem("authUrl"),
body,
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
})
)
.map((response: Response) => {
let returnedBody: any = response.json();
console.log("post returnedBody");
console.log(returnedBody);
if (typeof returnedBody.access_token !== 'undefined') {
localStorage.setItem('access_token', returnedBody.access_token);
localStorage.setItem('refresh_token', returnedBody.refresh_token);
console.log("Token Refreshed: refreshed access_token");
console.log(localStorage.getItem('access_token'));
return true;
} else {
return false;
}
});
另请注意,创建自定义 Http 服务可能不是最佳主意。
但也许您可以创建一个可以注入 http 的服务,因为您可能不需要通过身份验证就可以从 ajax 调用中获取一些简单的静态数据。
这也可以避免您遇到的超出递归调用堆栈的问题。
@Injectable()
export class MyAuhtHttpService{
constructor(private http:Http){}
}