必须刷新页面才能从api获取数据

时间:2019-11-22 21:24:07

标签: javascript angular typescript ionic-framework ionic4

我有一个有效的身份验证。流。只有一个奇怪的错误。当我以第一个用户身份登录时,我首先必须刷新页面才能在视图中获取数据。当我用该用户注销并下一个用户登录时,我的视图中仍然有user1的数据。如果我再次使用user2注销并使用user3登录,我的视图中将包含user2的数据。因此,似乎总是落后一步。我试图通过销毁订阅来解决此问题,但这并不能解决问题。同样,当我认为该错误开始时,我必须刷新页面以获取我的第一个用户数据,销毁订阅也不能成为错误解决方案。

这是我的代码:

auth.service:我在其中发布用户凭据,交还令牌,并存储它和user_id以便在视图中获取经过身份验证的用户数据。

import { Storage } from '@ionic/storage';
import { JwtHelperService } from '@auth0/angular-jwt';


export const TOKEN_KEY = 'access_token';
export const USERNAME_KEY = 'username_key';
export const USER_ID = 'user_id';
...
user = null;
refreshToken = null;
authenticationState = new BehaviorSubject(false);

  constructor(private storage: Storage, private helper: JwtHelperService) {
      this.checkToken();
   }

   checkToken() {
       this.storage.get(TOKEN_KEY).then(access => {
           if (access) {
               this.user = this.helper.decodeToken(access);
               this.authenticationState.next(true);
           }
       });
   }

   apilogin(username: string, password: string) {
    return this.http.post<any>(`${this.url}`, { username, password })
    .pipe(
        tap(res => {
            this.storage.set(TOKEN_KEY, res['access']);
            this.storage.set(USERNAME_KEY, username);
            this.storage.set(USER_ID, this.user['user_id']);
            this.user = this.helper.decodeToken(res['access']);
            console.log('my user: ', this.user);
            this.authenticationState.next(true);
        }));
}

apilogout() {
    this.storage.remove(USER_ID);
    this.storage.remove(USERNAME_KEY);
    this.storage.remove(TOKEN_KEY).then(() => {
    this.authenticationState.next(false);
   });
}

page.ts:这里获得在视图中显示的数据。 (用户服务只检索一个用户。最后我销毁了订阅)

import { Storage } from '@ionic/storage';
import { USER_ID } from 'src/app/services/auth.service';
import { SubscriptionLike } from 'rxjs';

  information = null;
  id: number;
  key: string;
  subscription: SubscriptionLike;

  constructor(private storage: Storage,  private activatedRoute: ActivatedRoute,
              private userService: UserService, private authService: AuthService) { }

  ngOnInit() {
    // How to get just the authenticated api?
      if (this.authService.authenticationState) {
      console.log(this.storage);
      this.storage.get(USER_ID).then(val => {
        this.id = val;
        this.subscription = this.userService.getUserDetails(this.id).subscribe(result => {
          this.information = result;
          console.log(this.information);
        });
      });
    }

  }

ngOnDestroy() {
this.subscription.unsubscribe();
this.information = null;
}

login.ts(处理我到主页的路由)

 // get return url from route parameters or default to '/'
    this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
...

apiSubmit() {
  console.log('Hello World');
  this.submitted = true;

  // if form is invalid => stop
  if (this.loginForm.invalid) {
      return;
  }
  this.isLoading = true;
  this.loadingEl.present();
  this.authService.apilogin(
  this.f.username,
  this.f.password)
      .pipe(tap(x => this.loadingEl.dismiss()),
      )
      .subscribe(
        data => {
          console.log('0');
          this.router.navigate([this.returnUrl]);
        },
        error => {
          console.log('1');
          this.loadingEl.dismiss();
          this.error = error;
          this.isLoading = false;
        }
      );
}

authGuard

export class AuthGuard implements CanActivate {
  constructor(
      private router: Router,
      private authService: AuthService
  ) { }


  canActivate(): boolean {
    return this.authService.isAuthenticated();
  }
} 

1 个答案:

答案 0 :(得分:3)

您实际上应该订阅此可观察的内容,而不是if (this.authService.authenticationState) {。另外,我们还需要记住,对离子Storage进行设置或从中删除实际上是异步的。我们需要等待动作已执行,然后再执行其他操作。我还建议不要在您的可观察对象上调用next,而只需调用可以执行检查的checkToken(),然后在BehaviorSubject上调用next

这应该有效:

服务:

import { BehaviorSubject, of, forkJoin } from 'rxjs';
import { tap, switchMap, map } from 'rxjs/operators';

// ...

private authenticationState = new BehaviorSubject(false);
public authenticationState$ = this.authenticationState.asObservable();

checkToken() {
  this.storage.get(TOKEN_KEY).then(access => {
    if (access) {
      this.authenticationState.next(true);
    } else {
      this.authenticationState.next(false);
    }
  });
}

apilogin(username: string, password: string) {
  return this.http.post<any>(`${this.url}`, { username, password }).pipe(
    // switch to inner observable
    switchMap((data: any) => {
      // run all in paralell
      return forkJoin(
        this.storage.set(TOKEN_KEY, 'access'),
        this.storage.set(USERNAME_KEY, 'username'),
        this.storage.set(USER_ID, 'id'),
      )
    }),
    // now we know for sure storage values have been set,
    // therefore call checkToken()
    tap(() => this.checkToken()),
  )
}

// seems you are not currently subscribing to this function in the
// component, so I guess you can subscribe here, but I'd subscribe in comp
apilogout() {
  forkJoin(
    this.storage.remove(USER_ID),
    this.storage.remove(REFRESH_TOKEN_KEY),
    this.storage.remove(USERNAME_KEY),
    this.storage.remove(TOKEN_KEY)
  ).subscribe(() => this.checkToken())
}

然后该组件将订阅您的BehaviorSubject并执行您需要做的一切:

ngOnInit(): void {
  this.sub = this.authService.authenticationState$.pipe(
    switchMap((data) => {
      return data ? this.storage.get(USER_ID) : of(null)
    }),
    switchMap((id: any) => {
      this.id = id;
      return id ? this.userService.getUserDetails(id) : of(null)
    })
  ).subscribe(val => {
    // do stuff!
  })
}

ngOnDestroy() {
  this.sub.unsubscribe();
}

PS。不要使用any,将数据输入模型:)