You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular扩展Nebular Auth实现邮箱验证时依赖注入报错求助

问题描述

我正在使用Nebular认证组件构建Angular应用,需要为注册功能添加自定义实现,并加入邮箱验证流程:用户注册时以inactive状态存入数据库,系统发送含验证链接的邮件,用户验证后账号标记为active。但陷入依赖注入问题,无法定位错误,相关代码及报错信息如下:

app.module.ts

@NgModule({
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        HttpClientModule,
        AppRoutingModule,
        SharedModule,
        ThemeModule.forRoot(),
        NbToastrModule.forRoot(),
        StoreModule.forRoot(reducers, {
            metaReducers,
            runtimeChecks: {
                strictStateImmutability: true,
                strictActionImmutability: true,
            },
        }),
        EffectsModule.forRoot(),
        NbAuthModule.forRoot({
            strategies: [
                CustomAuthStrategy.setup({
                    name: "email",
                    baseEndpoint: "/auth/",
                    login: {
                        endpoint: "login",
                        redirect: {
                            success: HOME_PAGE,
                            failure: null,
                        },
                    },
                    register: {
                        endpoint: "register",
                        redirect: {
                            success: null,
                            failure: null,
                        },
                    },
                    verifyemail: {
                        endpoint: "verify",
                    },
                    logout: {
                        endpoint: "",
                    },
                    requestPass: {
                        endpoint: "requestpass",
                    },
                    resetPass: {
                        endpoint: "resetpass",
                    },
                    token: {
                        class: NbAuthJWTToken,
                        key: "token",
                    },
                }),
            ],
            forms: {},
        }),
    ],
    declarations: [AppComponent],
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: AddTokenHeaderHttpRequestInterceptor,
            multi: true,
        },
        ApiService,
        CustomAuthStrategy,
        { provide: NbAuthService, useClass: CustomAuthService },
        NbTokenService,
        HttpClient,
    ],
    bootstrap: [AppComponent],
})
export class AppModule {}

custom-auth.service.ts

@Injectable({
    providedIn: "root",
})
export class CustomAuthService extends NbAuthService {
    strategies: NbAuthStrategy[];

    constructor(
        protected tokenService: NbTokenService,
        private injector: Injector,
    ) {
        // Initialize strategies after injector is available
        super(tokenService, []);
        this.strategies = this.injector.get(NbAuthStrategy, []);
    }

    verifyEmail(
        email: string,
        code: string,
        http: HttpClient,
    ): Observable<NbAuthResult> {
        const endpoint = "/auth/verify";
        return http.post(endpoint, { email, code }).pipe(
            map((res: any) => {
                return new NbAuthResult(
                    res.success,
                    res,
                    res.success ? "/auth/login" : null,
                    res.success ? [] : ["Verification failed."],
                    res.success
                        ? ["Email verified successfully. Please log in."]
                        : [],
                );
            }),
        );
    }
}

custom-auth.strategy.ts

export interface CustomAuthStrategyOptions
    extends NbPasswordAuthStrategyOptions {
    verifyemail?: {
        endpoint: string;
    };
}

@Injectable({
    providedIn: "root",
})
export class CustomAuthStrategy extends NbPasswordAuthStrategy {
    constructor(
        protected http: HttpClient,
        protected injector: Injector,
        route: ActivatedRoute,
        options: CustomAuthStrategyOptions,
    ) {
        super(http, route);
    }

    static setup(
        options: CustomAuthStrategyOptions,
    ): [typeof CustomAuthStrategy, CustomAuthStrategyOptions] {
        return [CustomAuthStrategy, options];
    }

    protected handleResponse(res: any): NbAuthResult {
        if (res.success) {
            return new NbAuthResult(
                true,
                res,
                this.getOption("register.redirect.success"),
                [],
                res.message,
            );
        } else {
            return new NbAuthResult(
                false,
                res,
                this.getOption("register.redirect.failure"),
                this.getOption("errors.getter")(res),
                this.getOption("messages.getter")(res),
            );
        }
    }

    register(data?: any): Observable<NbAuthResult> {
        return this.http.post(this.getActionEndpoint("register"), data).pipe(
            map((res) => this.handleResponse(res)),
            catchError((res: HttpErrorResponse) => {
                return of(
                    new NbAuthResult(
                        false,
                        res,
                        this.getOption("register.redirect.failure"),
                        this.getOption("errors.getter")(res),
                    ),
                );
            }),
        );
    }
}
报错信息

出现两个交替报错:

  1. Error: This constructor was not compatible with Dependency Injection.
    at Module.ɵɵinvalidFactory (core.mjs:10057:11)
    at Object.CustomAuthStrategy_Factory [as factory] (
    
  2. NullInjectorError: NullInjectorError: No provider for NB_AUTH_STRATEGIES!
解决方案

1. 修复CustomAuthStrategy构造函数兼容性

Nebular的NbPasswordAuthStrategy构造函数参数需严格匹配父类定义,当前构造函数多余的Injector和options参数导致DI无法创建实例。修改custom-auth.strategy.ts:

@Injectable({
    providedIn: "root",
})
export class CustomAuthStrategy extends NbPasswordAuthStrategy {
    constructor(
        protected http: HttpClient,
        protected route: ActivatedRoute
    ) {
        super(http, route);
    }

    // 其余代码保持不变
}

2. 修复CustomAuthService的策略注入

NbAuthService依赖NB_AUTH_STRATEGIES令牌,手动通过Injector获取策略的方式错误,改为直接注入该令牌。同时优化verifyEmail方法,将HttpClient注入构造函数而非作为参数传入:

import { NB_AUTH_STRATEGIES } from '@nebular/auth';
// ...其他导入

@Injectable({
    providedIn: "root",
})
export class CustomAuthService extends NbAuthService {
    constructor(
        protected tokenService: NbTokenService,
        @Inject(NB_AUTH_STRATEGIES) protected strategies: NbAuthStrategy[],
        protected http: HttpClient
    ) {
        super(tokenService, strategies);
    }

    verifyEmail(
        email: string,
        code: string,
    ): Observable<NbAuthResult> {
        const endpoint = "/auth/verify";
        return this.http.post(endpoint, { email, code }).pipe(
            map((res: any) => {
                return new NbAuthResult(
                    res.success,
                    res,
                    res.success ? "/auth/login" : null,
                    res.success ? [] : ["Verification failed."],
                    res.success
                        ? ["Email verified successfully. Please log in."]
                        : [],
                );
            }),
        );
    }
}

3. 清理AppModule的冗余配置

  • 移除providers中的CustomAuthStrategy,NbAuthModule.forRoot已通过strategies配置该策略,手动注册会导致冲突。
  • 移除HttpClient的手动注册,HttpClientModule已自动提供该服务。

修改后的app.module.ts providers部分:

providers: [
    {
        provide: HTTP_INTERCEPTORS,
        useClass: AddTokenHeaderHttpRequestInterceptor,
        multi: true,
    },
    ApiService,
    { provide: NbAuthService, useClass: CustomAuthService },
    NbTokenService,
],

4. 确认策略配置加载

CustomAuthStrategy的setup方法返回的配置会被NbAuthModule自动处理,创建策略实例并注入到NB_AUTH_STRATEGIES中,无需手动注册。

内容的提问来源于stack exchange,提问作者user176504

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 07:45:57