从 Router 加载 URL 以在 NgModule 中使用
Load URL from Router for use in NgModule
我正在设置 blue/green 部署,并试图根据用户当前正在查看的 url (redirectUri: this.router.url + '/callback',
) 更改下面的 redirectUri
。我收到具有以下配置的 Uncaught TypeError: Cannot read property 'router' of undefined
。
import { APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { CoreModule } from './@core/core.module';
import { AuthGuard } from './auth-guard.service';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { ThemeModule } from './@theme/theme.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NbOAuth2AuthStrategy,
NbAuthModule,
NbOAuth2ResponseType,
NbOAuth2GrantType,
NbAuthOAuth2Token,
} from '@nebular/auth';
import { OAuth2LoginComponent } from './auth/oauth2-login.component';
import { OAuth2CallbackComponent } from './auth/oauth2-callback.component';
import { environment } from '../environments/environment';
import { Router } from '@angular/router';
@NgModule({
declarations: [AppComponent, OAuth2LoginComponent, OAuth2CallbackComponent ],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
NgbModule.forRoot(),
ThemeModule.forRoot(),
CoreModule.forRoot(),
NbAuthModule.forRoot({
forms: {},
strategies: [
NbOAuth2AuthStrategy.setup({
baseEndpoint: environment.authUrl,
name: 'cognito',
clientId: environment.clientId,
authorize: {
endpoint: '/oauth2/authorize',
responseType: NbOAuth2ResponseType.CODE,
scope: 'aws.cognito.signin.user.admin',
redirectUri: this.router.url + '/callback',
},
redirect: {
success: '/pages/dashboard',
},
token: {
endpoint: '/oauth2/token',
grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
class: NbAuthOAuth2Token,
redirectUri: this.router.url + '/callback',
},
refresh: {
endpoint: 'refresh-token',
grantType: NbOAuth2GrantType.REFRESH_TOKEN,
},
}),
],
}),
AppRoutingModule,
],
bootstrap: [AppComponent],
providers: [
AuthGuard,
{ provide: APP_BASE_HREF, useValue: '/' },
],
})
export class AppModule {
constructor(private router: Router) {}
}
我也尝试过使用 redirectUri: window.location.origin + '/callback'
,它在本地工作,但在为生产构建时为 null。
请注意,class level decorators 在创建 class 的任何实例之前应用于构造函数。因此路由器 属性 不可用于装饰器。例子中的this.router.url + '/callback'
反而引用了全局的this
,奇怪的是没有编译错误。
关于window.location
,在aot
编译模式下,这是prod构建的默认模式,装饰器中的表达式在编译时由Angular编译器执行,所以window.location
在那里不可用。看看这个 GitHub 问题:AOT replaces window.location object to null
作为解决方法,您可以动态初始化 NbOAuth2AuthStrategy
,例如:
@NgModule({
imports: [
...
NbAuthModule.forRoot({
strategies: [
NbOAuth2AuthStrategy.setup({
name: 'cognito'
})
],
...
})
],
...
})
export class AppModule {
constructor(
authService: NbAuthService, // force construction of the auth service
oauthStrategy: NbOAuth2AuthStrategy
) {
// window.location should be available here
this.oauthStrategy.setOpitions({
name: 'cognito',
...
});
}
}
我发现,将 NbAuthService
添加到构造函数参数以及 NbOAuth2AuthStrategy
很重要。看来service在构造的时候就初始化了strategy,所以应该在strategy初始化之前先构造一下。
另请注意,setOptions()
方法完全覆盖了模块装饰器的选项,因此整个策略初始化应从装饰器移至构造器。
我还发现了 this GitHub 问题,这帮助我找到了正确的解决方案。
如果你想做类似的事情,你可以使用注入令牌并创建一个 returns 你想要的值的工厂函数。这将是 运行 在浏览器中,您将看到您想要的值。
const REDIRECT_URI = new InjectionToken('REDIRECT_URI');
export function redirectUriFactory {
return `${location.protocol}/${location.host}/callback`
}
@NgModule(...)
class MyModule {
forRoot() {
return {
ngModule: MyModule,
providers: [
{ provide: REDIRECT_URI, useFactory: redirectUriFactory }
]
}
}
}
我没有测试这个,但是 InjectionToken with Factory 是进入 AOT 的必经之路
在您提供的代码示例中,您似乎试图在实例化 class 'AppModule
' 之前在 @NgModule
注释中访问路由器对象 this.router
.
实例变量在注释中不可用。 Angular 使用 [依赖注入][1] 通过构造函数提供对象实例。
看你的情况,虽然我不太了解 NbOAuth2AuthStrategy
模块,但你可以在 import
中定义模块后从构造函数中寻找配置身份验证策略的选项@NgModule
注释部分。考虑这个代码片段,它可能对你有帮助。在下面用 << >>
标记的代码中查找占位符
NbAuthModule.forRoot({
forms: {},
strategies: [
NbOAuth2AuthStrategy.setup({
baseEndpoint: environment.authUrl,
name: 'cognito',
clientId: environment.clientId,
authorize: {
endpoint: '/oauth2/authorize',
responseType: NbOAuth2ResponseType.CODE,
scope: 'aws.cognito.signin.user.admin',
redirectUri: '<<SET SOME DEFAULT URL>>',
},
redirect: {
success: '/pages/dashboard',
},
token: {
endpoint: '/oauth2/token',
grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
class: NbAuthOAuth2Token,
redirectUri: '<<SET SOME DEFAULT URL>>',
},
refresh: {
endpoint: 'refresh-token',
grantType: NbOAuth2GrantType.REFRESH_TOKEN,
},
}),
...
export class AppModule {
constructor(private router: Router) {
<<Reconfigure your NbOAuth2AuthStrategy>>
NbOAuth2AuthStrategy.setup....
}
}
Hope the solution works for you.
[1]: https://angular.io/guide/dependency-injection
您的解决方案将不起作用,因为正如 Valeriy Katkov 所提到的,class 级别装饰器在创建 class 的任何实例之前应用于构造函数。因此,您将无法将路由器注入装饰器。
为了能够注入路由器,您需要将您的实现移动到 class 中。可以通过 NbAuthStrategy
实例的 setOptions
方法,但是,有一些问题需要克服,例如 here or here。
为了使其工作,您应该将策略配置移动到扩展 NbAuthComponent
(这很重要)的组件,例如:
export class AppComponent extends NbAuthComponent {
constructor(auth: NbAuthService,
location: Location,
private router: Router,
authStrategy: NbPasswordAuthStrategy) {
super(auth, location);
authStrategy.setOptions({
name: 'username',
login: {
alwaysFail: false,
endpoint: 'test',
method: 'post',
requireValidToken: false,
redirect: {
success: this.router.url + '/success-callback',
failure: this.location.path() + '/callback'
},
defaultErrors: ['Damn'],
defaultMessages: ['Great'],
},
});
}
}
此外,我建议您使用 this.location.path()
而不是 this.router.url
,因为 this.router.url
不会为您提供您所在的 url,而是 url的组件级别。 this.location.path()
将为您提供您所在页面的完整路径。
这是一个 StackBlitz example 的有效解决方案。
请让我知道您是否仍然不清楚或需要一些额外的细节。
我正在设置 blue/green 部署,并试图根据用户当前正在查看的 url (redirectUri: this.router.url + '/callback',
) 更改下面的 redirectUri
。我收到具有以下配置的 Uncaught TypeError: Cannot read property 'router' of undefined
。
import { APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { CoreModule } from './@core/core.module';
import { AuthGuard } from './auth-guard.service';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { ThemeModule } from './@theme/theme.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NbOAuth2AuthStrategy,
NbAuthModule,
NbOAuth2ResponseType,
NbOAuth2GrantType,
NbAuthOAuth2Token,
} from '@nebular/auth';
import { OAuth2LoginComponent } from './auth/oauth2-login.component';
import { OAuth2CallbackComponent } from './auth/oauth2-callback.component';
import { environment } from '../environments/environment';
import { Router } from '@angular/router';
@NgModule({
declarations: [AppComponent, OAuth2LoginComponent, OAuth2CallbackComponent ],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
NgbModule.forRoot(),
ThemeModule.forRoot(),
CoreModule.forRoot(),
NbAuthModule.forRoot({
forms: {},
strategies: [
NbOAuth2AuthStrategy.setup({
baseEndpoint: environment.authUrl,
name: 'cognito',
clientId: environment.clientId,
authorize: {
endpoint: '/oauth2/authorize',
responseType: NbOAuth2ResponseType.CODE,
scope: 'aws.cognito.signin.user.admin',
redirectUri: this.router.url + '/callback',
},
redirect: {
success: '/pages/dashboard',
},
token: {
endpoint: '/oauth2/token',
grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
class: NbAuthOAuth2Token,
redirectUri: this.router.url + '/callback',
},
refresh: {
endpoint: 'refresh-token',
grantType: NbOAuth2GrantType.REFRESH_TOKEN,
},
}),
],
}),
AppRoutingModule,
],
bootstrap: [AppComponent],
providers: [
AuthGuard,
{ provide: APP_BASE_HREF, useValue: '/' },
],
})
export class AppModule {
constructor(private router: Router) {}
}
我也尝试过使用 redirectUri: window.location.origin + '/callback'
,它在本地工作,但在为生产构建时为 null。
请注意,class level decorators 在创建 class 的任何实例之前应用于构造函数。因此路由器 属性 不可用于装饰器。例子中的this.router.url + '/callback'
反而引用了全局的this
,奇怪的是没有编译错误。
关于window.location
,在aot
编译模式下,这是prod构建的默认模式,装饰器中的表达式在编译时由Angular编译器执行,所以window.location
在那里不可用。看看这个 GitHub 问题:AOT replaces window.location object to null
作为解决方法,您可以动态初始化 NbOAuth2AuthStrategy
,例如:
@NgModule({
imports: [
...
NbAuthModule.forRoot({
strategies: [
NbOAuth2AuthStrategy.setup({
name: 'cognito'
})
],
...
})
],
...
})
export class AppModule {
constructor(
authService: NbAuthService, // force construction of the auth service
oauthStrategy: NbOAuth2AuthStrategy
) {
// window.location should be available here
this.oauthStrategy.setOpitions({
name: 'cognito',
...
});
}
}
我发现,将 NbAuthService
添加到构造函数参数以及 NbOAuth2AuthStrategy
很重要。看来service在构造的时候就初始化了strategy,所以应该在strategy初始化之前先构造一下。
另请注意,setOptions()
方法完全覆盖了模块装饰器的选项,因此整个策略初始化应从装饰器移至构造器。
我还发现了 this GitHub 问题,这帮助我找到了正确的解决方案。
如果你想做类似的事情,你可以使用注入令牌并创建一个 returns 你想要的值的工厂函数。这将是 运行 在浏览器中,您将看到您想要的值。
const REDIRECT_URI = new InjectionToken('REDIRECT_URI');
export function redirectUriFactory {
return `${location.protocol}/${location.host}/callback`
}
@NgModule(...)
class MyModule {
forRoot() {
return {
ngModule: MyModule,
providers: [
{ provide: REDIRECT_URI, useFactory: redirectUriFactory }
]
}
}
}
我没有测试这个,但是 InjectionToken with Factory 是进入 AOT 的必经之路
在您提供的代码示例中,您似乎试图在实例化 class 'AppModule
' 之前在 @NgModule
注释中访问路由器对象 this.router
.
实例变量在注释中不可用。 Angular 使用 [依赖注入][1] 通过构造函数提供对象实例。
看你的情况,虽然我不太了解 NbOAuth2AuthStrategy
模块,但你可以在 import
中定义模块后从构造函数中寻找配置身份验证策略的选项@NgModule
注释部分。考虑这个代码片段,它可能对你有帮助。在下面用 << >>
NbAuthModule.forRoot({
forms: {},
strategies: [
NbOAuth2AuthStrategy.setup({
baseEndpoint: environment.authUrl,
name: 'cognito',
clientId: environment.clientId,
authorize: {
endpoint: '/oauth2/authorize',
responseType: NbOAuth2ResponseType.CODE,
scope: 'aws.cognito.signin.user.admin',
redirectUri: '<<SET SOME DEFAULT URL>>',
},
redirect: {
success: '/pages/dashboard',
},
token: {
endpoint: '/oauth2/token',
grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
class: NbAuthOAuth2Token,
redirectUri: '<<SET SOME DEFAULT URL>>',
},
refresh: {
endpoint: 'refresh-token',
grantType: NbOAuth2GrantType.REFRESH_TOKEN,
},
}),
...
export class AppModule {
constructor(private router: Router) {
<<Reconfigure your NbOAuth2AuthStrategy>>
NbOAuth2AuthStrategy.setup....
}
}
Hope the solution works for you.
[1]: https://angular.io/guide/dependency-injection
您的解决方案将不起作用,因为正如 Valeriy Katkov 所提到的,class 级别装饰器在创建 class 的任何实例之前应用于构造函数。因此,您将无法将路由器注入装饰器。
为了能够注入路由器,您需要将您的实现移动到 class 中。可以通过 NbAuthStrategy
实例的 setOptions
方法,但是,有一些问题需要克服,例如 here or here。
为了使其工作,您应该将策略配置移动到扩展 NbAuthComponent
(这很重要)的组件,例如:
export class AppComponent extends NbAuthComponent {
constructor(auth: NbAuthService,
location: Location,
private router: Router,
authStrategy: NbPasswordAuthStrategy) {
super(auth, location);
authStrategy.setOptions({
name: 'username',
login: {
alwaysFail: false,
endpoint: 'test',
method: 'post',
requireValidToken: false,
redirect: {
success: this.router.url + '/success-callback',
failure: this.location.path() + '/callback'
},
defaultErrors: ['Damn'],
defaultMessages: ['Great'],
},
});
}
}
此外,我建议您使用 this.location.path()
而不是 this.router.url
,因为 this.router.url
不会为您提供您所在的 url,而是 url的组件级别。 this.location.path()
将为您提供您所在页面的完整路径。
这是一个 StackBlitz example 的有效解决方案。
请让我知道您是否仍然不清楚或需要一些额外的细节。