Angular 从.net core获取广告用户信息时出现问题

Angular 从.net core获取广告用户信息时出现问题,angular,asp.net-core,active-directory,Angular,Asp.net Core,Active Directory,我的应用程序由两个独立的项目组成:一个.NET核心后端 和一个有角度的前端 我已经成功地添加了windows AD身份验证,我正在Angular应用程序中获取用户信息,但只有在刷新页面之后 打开第一页时,我的用户为空 ERROR TypeError: Cannot read property 'name' of null 刷新后,我能够获得用户信息。实际发生了什么,我应该如何解决这个问题 public Task < AdUser > GetAdUser(Guid guid) {

我的应用程序由两个独立的项目组成:一个.NET核心后端 和一个有角度的前端

我已经成功地添加了windows AD身份验证,我正在Angular应用程序中获取用户信息,但只有在刷新页面之后

打开第一页时,我的用户为空

ERROR TypeError: Cannot read property 'name' of null
刷新后,我能够获得用户信息。实际发生了什么,我应该如何解决这个问题

public Task < AdUser > GetAdUser(Guid guid) {
  return Task.Run(() => {
    try {
      PrincipalContext context = new PrincipalContext(ContextType.Domain);
      UserPrincipal principal = new UserPrincipal(context);

      if (context != null) {
        principal = UserPrincipal.FindByIdentity(context, IdentityType.Guid,
          guid.ToString());
      }

      return AdUser.CastToAdUser(principal);
    } catch (Exception ex) {
      throw new Exception("Error retrieving AD User", ex);
    }
  });
}
app.component.rs top-navigation.component.ts 最后,我在顶部导航组件中获取用户数据,因为我需要在其中显示名字和姓氏

编辑:添加新代码

app-routing.module.ts app.component.ts top-navigation.component.ts 编辑:最终解决方案

app-routing.module.ts
好的,我会试着解释这一切是如何运作的。您的问题是,在localStorage尝试获取数据之后,组件中的getCurrentUser会执行

如果您的AppComponent是应用程序的根组件,top navigation组件在应用程序组件html中声明,那么您需要在AppComponent启动之前确保数据在那里

因此,所发生的是(一个可能的时间线,因为javascript的异步性质意味着有时某些步骤将以不同的顺序执行):

  • 应用程序被路由到您的核心路由
  • AppComponent的构造函数执行
  • TopNavigationComponent的构造函数将执行
  • getCurrentUser()http调用将启动
  • TopNavigationComponent尝试从localStorage获取值(出于安全原因,最好使用SessionStorage),但失败
  • getCurrentUser()http调用结束,AppComponent设置localStorage值
  • 你刷新了。当你到达第五步时。已从步骤6开始设置本地存储
  • 最好的解决办法是使用防护装置。如果您需要将ADUser用作安全功能,那么这对您的情况更为有效。如果防护失败,则不会转换到AppComponent

    声明一个防护,如果数据在那里,则转到应用程序组件,防护将确保您在本地存储(或会话存储)中有ADUser数据

    解析器(另一种可能的解决方案)将始终在加载AppComponent之前运行,并且不会阻塞,从而确保在应用程序响应时您拥有数据

    有关防护和冲突解决程序的详细信息

    看看下面的防护装置:

    @Injectable()
    export class AuthGuard implements CanActivate {
      constructor(
        private adUserService: ADUserService, 
        private router: Router
      ) {}
    
      canActivate(next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot): Observable<boolean> {
        // We will return a true if the user is logged in
        // or false otherwise
        return this.adUserService.login().pipe(
          map((x) => {
            // Since my service returned succesfully I will set the
            // locale storage with the appropriate data
            localStorage.setItem('user', JSON.stringify(x));
            // I will also return true to let the router module
            // know that it can proceed
            return true;
          }),
          catchError(() => {
            // If there was an error, I will return false, so the
            // router module will not allow the transition
            // if I want to, I can add the router module to transition
            // to a login page
            return of(false)
          })
        );
      }
    }
    
    @Injectable()
    导出类AuthGuard实现了CanActivate{
    建造师(
    私人adUserService:adUserService,
    专用路由器
    ) {}
    canActivate(下一步:ActivatedRouteSnapshot,
    状态:RouterStateSnapshot):可观察{
    //如果用户登录,我们将返回true
    //否则就错了
    返回此.adUserService.login()管道(
    地图((x)=>{
    //既然我的服务成功返回,我将设置
    //具有适当数据的区域设置存储
    setItem('user',JSON.stringify(x));
    //我还将返回true,让路由器模块
    //知道它可以继续
    返回true;
    }),
    捕获错误(()=>{
    //如果有错误,我将返回false,因此
    //路由器模块将不允许转换
    //如果我想,我可以将路由器模块添加到transition
    //转到登录页面
    返回(假)
    })
    );
    }
    }
    
    您可以在此处查看完整的解决方案:


    要查看正在运行的区域设置存储,请尝试url。

    重新措辞并格式化代码段感谢提供的解决方案。我已经设置了Guard服务,我在其中检查用户对象是否为null,但我不知道如果它为null该怎么办。。。你说“如果数据在那里,那么转到应用程序组件,警卫将确保你在本地存储中有ADUser数据”,如何继续和不继续转到主应用程序组件。。。希望你明白我的意思,我当然明白。检查stackblitz url以获取完整解决方案。我没有包括顶部导航。如果答案对你有帮助,一定要投赞成票。对不起,我对此有一些问题,即使你的stackblitz演示也很好。。。在我的代码中,我的应用程序总是进入CanActivate,并试图通过this.adUserService.login()获取数据,但出于某种原因,我的顶部导航正在尝试呈现数据,当然用户为空。原因是什么?请记住,我正在top-navigation.component.ts中的ngOnInit下调用“this.user=JSON.parse(localStorage.getItem('user')”),但显然它是空的。。。我还需要使用解析器吗?能否在代码中共享路由器配置和AppComponent/TopNavigation?我已用您要求的代码更新了我的问题。
    this.identityService.getCurrentUser()
    .subscribe(res => {
    localStorage.setItem('user', JSON.stringify(res));
    });
    
    user = JSON.parse(localStorage.getItem('user'));
    
        const appRoutes: Routes = [
       {
        path: 'home',
        component: HomeComponent,
        data: { title: 'Home' },
        canActivate: [AuthGuardService]
      },
      {
        path: 'history',
        component: HistoryComponent,
        data: { title: 'History' }
      },
      {
        path: '404',
        component: NotFoundErrorComponent,
        data: { title: 'Error 404' }
      },
      {
        path: '',
        component: HomeComponent,
        pathMatch: 'full'
      },
      {
        path: '**',
        redirectTo: '/404',
        pathMatch: 'full',
        data: { title: 'Error 404' }
      }
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(appRoutes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    
        @Component({
         selector: 'app-root',
         templateUrl: './app.component.html',
         styleUrls: ['./app.component.scss']
        })
        export class AppComponent implements OnInit {
         title = 'Home';
    
        constructor(private titleService: Title,
                  private router: Router,
                  private activatedRoute: ActivatedRoute { }
    
        ngOnInit() {
        this.router.events.pipe(
          filter(event => event instanceof NavigationEnd),
          map(() => this.activatedRoute),
          map((route) => {
            while (route.firstChild) { route = route.firstChild; }
            return route;
          }),
          filter((route) => route.outlet === 'primary'),
          mergeMap((route) => route.data))
          .subscribe((event) => this.titleService.setTitle(event.title));
         }
         }
    
        export class TopNavigationComponent implements OnInit {
          constructor(private router: Router) { }
    
          user;
    
          ngOnInit() {
           this.user = JSON.parse(localStorage.getItem('user'));
          }
         }
    
    const appRoutes: Routes = [
    {
    path: '',
    component: TopNavComponent,
    resolve: { userData: IdentityResolverService },
    children: [
      {
        path: 'home',
        component: HomeComponent,
        data: { title: 'Home' },
        canActivate: [AuthGuardService]
      },
      {
        path: 'history',
        component: HistoryComponent,
        data: { title: 'History' },
        canActivate: [AuthGuardService]
      },
      {
        path: '404',
        component: NotFoundErrorComponent,
        data: { title: 'Error 404' },
        canActivate: [AuthGuardService]
      },
      {
        path: '',
        redirectTo: 'home',
        pathMatch: 'full',
        canActivate: [AuthGuardService]
      },
      {
        path: '**',
        redirectTo: '/404',
        pathMatch: 'full',
        data: { title: 'Error 404' },
        canActivate: [AuthGuardService]
      }
    ]
    };
    ]
    
    @Injectable()
    export class AuthGuard implements CanActivate {
      constructor(
        private adUserService: ADUserService, 
        private router: Router
      ) {}
    
      canActivate(next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot): Observable<boolean> {
        // We will return a true if the user is logged in
        // or false otherwise
        return this.adUserService.login().pipe(
          map((x) => {
            // Since my service returned succesfully I will set the
            // locale storage with the appropriate data
            localStorage.setItem('user', JSON.stringify(x));
            // I will also return true to let the router module
            // know that it can proceed
            return true;
          }),
          catchError(() => {
            // If there was an error, I will return false, so the
            // router module will not allow the transition
            // if I want to, I can add the router module to transition
            // to a login page
            return of(false)
          })
        );
      }
    }