首页 文章

用于延迟加载模块的Angular 4.3拦截器

提问于
浏览
6

在延迟加载的功能模块和功能子模块中使用核心模块服务的最佳做法是什么 .
根据Angular样式指南,我有以下内容

app
   -core
       - core.module.ts
       -logger.service.ts
       -token-interceptor.service.ts
       -authentication.service.ts
   -shared
       -shared.module.ts
   -base module (my feature base , lazy loaded with router-outlet)
     -base.module.ts
     -base.routing.module.ts
     -base
       -base.component.ts
     -admin (lazy loaded , child module of base module)
      -admin.module.ts
      -admin.routing.ts
      -admin-component.ts 
     -report(lazy loaded , child module of base module, sibling of admin)
      -report.module.ts
      -report.routing.ts
      -report-component.ts

如果我在所有功能模块中添加TokenInterceptorService作为提供程序,那么HTTP拦截器可以正常工作 . 当我在App模块中添加它时(但不是在延迟加载的功能模块中)它不会拦截延迟加载的功能模块中触发的http请求 .

使用核心模块中声明的服务/拦截器的最佳实践是什么?

app.module.ts

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
  ],
  imports: [
    BrowserModule,BrowserAnimationsModule, CoreModule, AppRoutingModule, FormsModule
  ], providers: [{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptorService, multi: true }],
  bootstrap: [AppComponent]
})
export class AppModule { }

APP-routing.module.ts

@NgModule({
    imports: [
        RouterModule.forRoot([
            { path: 'login', component: LoginComponent },
            { path: '', redirectTo: 'base', pathMatch: 'full' },                   
            {  path: 'base', loadChildren: 'app/base/base.module#BaseModule'  }            
        ])
    ],
    exports: [
        RouterModule
    ]
})
export class AppRoutingModule {
}

core.module.ts

@NgModule({
  imports: [
    CommonModule, HttpModule,  
  ],
  declarations: [],
  providers: [LoggerService, AuthenticationService]
})
export class CoreModule {
  constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
    throwIfAlreadyLoaded(parentModule, 'CoreModule');
  }
}

令牌interceptor.service.ts

import { Injectable } from '@angular/core';
    import { Router } from '@angular/router';
    import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
    import { Observable } from 'rxjs/Observable';
    import 'rxjs/add/operator/do';

    import { AuthenticationService } from './authentication.service';

    @Injectable()
    export class TokenInterceptorService implements HttpInterceptor {


      constructor(public auth: AuthenticationService, private router: Router) { }

      intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        console.log('Adding authorization header')

        request = request.clone({
          setHeaders: { authorization: this.auth.getToken() }
        });
        console.log('Added authorization header')
        return next.handle(request).do(event => { }, (err: HttpErrorResponse) => {
          console.log("Error ===>", err);
          if (err.error instanceof Error) {
            // A client-side or network error occurred. Handle it accordingly.
            console.log('An error occurred:', err.error.message);
          } else if (err.status == 401) {

            console.log('Status 401  unautorized');
            this.router.navigate(['/login']);
          } else {
            // The backend returned an unsuccessful response code.
            // The response body may contain clues as to what went wrong,
            console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
          }

          return Observable.throw(new Error('Your custom error'));
        });;
      }

    }

base.module.ts

@NgModule({
      imports: [
        CommonModule, BaseRoutingModule
      ],
      declarations: [BaseComponent],
      providers: [],
    })
    export class BaseModule {


      constructor( @Optional() @SkipSelf() parentModule: BaseModule) {

        if (parentModule) {
          throw new Error(
            'BaseModule is already loaded. Import it in the AppModule only');
        }
      }
    }

base.routing.module.ts

@NgModule({
    imports: [
        RouterModule.forChild([
            {
                path: '', component: BaseComponent,
                children: [

                    { path: '', redirectTo: 'admin', pathMatch: 'full' },
                    { path: 'admin', loadChildren: 'app/base/admin/admin.module#AdminModule' },                         
                ]
            }])
    ],
    exports: [
        RouterModule
    ]
})
export class BaseRoutingModule {
}

admin.module.ts

@NgModule({
  imports: [
    CommonModule, FormsModule, HttpClientModule, AdminRoutingModule,BusyModule
  ],
  declarations: [UserListComponent, UserComponent, MenuListComponent, MenuComponent, CodeListComponent, CodeComponent],
  providers: [CodeService, UserService, MenuService,{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptorService, multi: true }]
})
export class AdminModule { }

admin.routing.module.ts

@NgModule({
    imports: [
        RouterModule.forChild([

            {
                path: '',
                children: [
                    { path: '', redirectTo:'code-list', pathMatch: 'full'  },
                    { path: 'code-list', component: CodeListComponent },
                    { path: 'code/:id', component: CodeComponent },

                    { path: 'user-list', component: UserListComponent },
                    { path: 'user/:id', component: UserComponent },

                    { path: 'menu-list', component: MenuListComponent },
                    { path: 'menu/:id', component: MenuComponent },                    
                ]
            }
        ])
    ],
    exports: [
        RouterModule
    ]
})
export class AdminRoutingModule {
}

1 回答

  • 3

    您不必为拦截器创建提供程序 . 您应该使用 forRoot() 导出 CoreModule

    @NgModule({
      imports: [
        CommonModule,
        HttpClientModule,
        TranslateModule.forRoot({
          loader: {
            provide: TranslateLoader,
            useFactory: HttpLoaderFactory,
            deps: [HttpClient]
          }
        }),
        RouterModule.forRoot(
          [],
          {enableTracing: true}
        ),
      ],
      declarations: [],
      providers: [DatePipe]
    })
    export class CoreModule {
      constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
        if (parentModule) {
          throw new Error(
            'CoreModule is already loaded. Import it in the AppModule only');
        }
      }
    
      static forRoot(): ModuleWithProviders {
        return {
          ngModule: CoreModule,
          providers: [
            {provide: 'Window', useValue: window},
            {provide: HTTP_INTERCEPTORS, useClass: RequestInterceptor, multi: true},
            SORT_TYPES_PROVIDER,
            ApiService,
            AnimationService,
            BillingService,
            UserService,
            ...
          ]
        };
      }
    }
    

    然后将其导入 AppModule 并完全忘记 CoreModule 进口 . 这只是必须明确使用的一个地方 . 所有延迟加载的模块都将通过DI获取您的服务等 .

    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        SharedModule,
        CoreModule.forRoot(),
        FeaturesModule,
        PublicModule,
        RouterModule.forRoot([])
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule {
    }
    

    您也不需要为每个模块创建单独的路由模块 . 只需导出RouterModule.forChild返回的内容,并在需要它的模块的 imports 中使用它 .

    export const publicRouting: ModuleWithProviders = RouterModule.forChild([
      {
        path: 'login',
        pathMatch: 'full',
        component: SignInComponent,
        data: {
          breadcrumb: 'LGN_TL'
        },
        canActivate: [AuthenticatedGuard]
      },
      {
        path: '',
        component: GlobalComponent,
        loadChildren: '../protected/protected.module#ProtectedModule',
        canLoad: [AuthCanLoadGuard]
      },
      {path: '**', component: PageNotFoundComponent}
    ]);
    

    UPD . 建议路由不是样式指南 . 像以前一样使用 RoutingModulehttps://angular.io/guide/styleguide#angular-ngmodule-names

相关问题