根据服务器是否运行注入不同的服务
Inject different services based on whether the server is running
我想在后端服务器关闭时自动提供“存根”服务。目前我通过手动更改配置设置“isRunning”来做到这一点。但出于多种原因,我希望在不编辑代码或配置文件的情况下完成此检查。
let isRunning: boolean = true;
...
providers: [
{
provide: MyService,
useClass: isRunning ? MyService : MyServiceStub
},
...
],
我尝试使用 APP_INITIALIZER 注入令牌,这样我就可以在加载应用程序之前“ping”服务器并在选择提供程序时测试结果。 (这段代码既是on Github and can also be viewed in this Stackblitz.
function IsRunning(){
return AppInitService.isWebServerRunning();
}
...
providers: [
{
provide: MyService,
useClass: IsRunning() ? MyService : MyServiceStub
}
],
但我遇到的问题是“isRunning()”returns 它的结果是在框架已经选择并将 MyService 添加到依赖项注入器之后。因此,即使上面的“isRunning()”returns false,它仍然选择 MyService 而不是 MyServiceStub。
如果你 运行 Stackblitz,你可以从控制台日志中看到这是真的。 MyService构造函数中的console.log语句在IsRunning()之前输出returns.
有没有其他方法可以做到这一点。我是否可以在 IsRunning() 函数中使用代码手动注入提供者,而不是依赖 @NgModule 中的提供者数组。
APP_INITIALIZER 令牌及其提供服务的工厂在 AppInitModule 中实现:
export function pingFactory(appInitService: AppInitService) {
return () => appInitService.pingServer();
}
@NgModule({
imports: [HttpClientModule],
providers: [
AppInitService,
{ provide: APP_INITIALIZER, useFactory: pingFactory, deps: [AppInitService], multi: true },
],
})
export class AppInitModule {}
这里是 AppInitService 的代码:
@Injectable({providedIn: 'root'})
export class AppInitService {
constructor(private httpClient: HttpClient) {}
static isRunning: boolean | null = null;
static async isWebServerRunning() {
logMsg("isWebServerRunning Enter");
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
// check every 50 milliseconds for change in "isRunning" status
while (AppInitService.isRunning === null) {
logMsg("isWebServerRunning in delay loop");
await delay(50);
}
let isrunning = AppInitService.isRunning;
logMsg("isWebServerRunning Exit -isRunnning = " + String(isrunning));
return isrunning;
}
pingServer(): Promise<any> {
logMsg("pingServer. Enter");
const promise = this.httpClient
.get(server)
.toPromise()
.then((settings) => {
logMsg("pingServer Got server response");
AppInitService.isRunning = true;
return true;
})
.catch((err) => {
logMsg("pingServer No server Response");
AppInitService.isRunning = false;
err;
});
return promise;
}
}
我检查了 stackblitz 示例,发现您在 appinit.module.ts
中有 APP_INITIALIZER,但它必须导入到根模块 (app.module.ts
) 中并重构了一些代码。我为服务创建了一个管理器模块和一个工厂,以便根据我们的条件初始化服务器。
Here my solution on Stackblitz
我添加了一些文件夹,并且在每个文件中都写了解释作为注释。
app.module.ts
// imports omitted...
export function pingFactory(appInitService: AppInitService) {
return () => appInitService.pingServer();
}
@NgModule({
declarations: [AppComponent],
// MyServiceManagerModule must be imported calling forRoot on our root module
// this module internally handle which service will be used
imports: [BrowserModule, HttpClientModule, MyServiceManagerModule.forRoot()],
providers: [
// our APP_INITIALIZER must be imported on our root module too
{
provide: APP_INITIALIZER,
useFactory: pingFactory,
deps: [AppInitService],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
my-service-manager.module.ts
// imports omitted...
// this factory needs AppInitService to know if our web server is running
// in order to select our service
export function myServiceFactory(
appInitService: AppInitService
): MyService | MyServiceStub {
return appInitService.isRunning ? new MyService() : new MyServiceStub();
}
@NgModule()
export class MyServiceManagerModule {
static forRoot(): ModuleWithProviders<MyServiceManagerModule> {
return {
ngModule: MyServiceManagerModule,
providers: [
// our service factory
{
provide: MyServiceLoader,
useFactory: myServiceFactory,
deps: [AppInitService]
}
]
};
}
}
my-service-loader.ts
// imports omitted...
// our abstract class
@Injectable()
export abstract class MyServiceLoader {
abstract printTime(): void;
abstract getNow(): string;
}
my-service-stub.ts
// imports omitted...
// must extend MyServiceLoader which is an abstract class
@Injectable()
export class MyServiceStub extends MyServiceLoader {
printTime(): void {
let time = this.getNow();
console.log("MyServiceStub:printTime ", time);
}
getNow(): string {
let now = Date.now();
let sec = Math.floor(now / 1000) % 100;
let ms = now % 1000;
return sec.toString() + ":" + ms.toString();
}
}
my-service.ts
// imports omitted...
// must extend MyServiceLoader which is an abstract class
@Injectable()
export class MyService extends MyServiceLoader {
printTime(): void {
let time = this.getNow();
console.log("MyService:printTime ", time);
}
getNow(): string {
let now = Date.now();
let sec = Math.floor(now / 1000) % 100;
let ms = now % 1000;
return sec.toString() + ":" + ms.toString();
}
}
app.component.ts
import { Component } from "@angular/core";
import { MyServiceLoader } from "./my-service/my-service-loader";
@Component({
selector: "app-root",
template: `
<h2>Test for server running</h2>
`
})
export class AppComponent {
title = "testasync";
// MyServiceLoader must be imported
constructor(private myService: MyServiceLoader) {
console.log("AppComponent:ngOnInit", this.getNow());
this.myService.printTime();
}
getNow(): string {
let now = Date.now();
let sec = Math.floor(now / 1000) % 100;
let ms = now % 1000;
return sec.toString() + ":" + ms.toString();
}
}
我想在后端服务器关闭时自动提供“存根”服务。目前我通过手动更改配置设置“isRunning”来做到这一点。但出于多种原因,我希望在不编辑代码或配置文件的情况下完成此检查。
let isRunning: boolean = true;
...
providers: [
{
provide: MyService,
useClass: isRunning ? MyService : MyServiceStub
},
...
],
我尝试使用 APP_INITIALIZER 注入令牌,这样我就可以在加载应用程序之前“ping”服务器并在选择提供程序时测试结果。 (这段代码既是on Github and can also be viewed in this Stackblitz.
function IsRunning(){
return AppInitService.isWebServerRunning();
}
...
providers: [
{
provide: MyService,
useClass: IsRunning() ? MyService : MyServiceStub
}
],
但我遇到的问题是“isRunning()”returns 它的结果是在框架已经选择并将 MyService 添加到依赖项注入器之后。因此,即使上面的“isRunning()”returns false,它仍然选择 MyService 而不是 MyServiceStub。
如果你 运行 Stackblitz,你可以从控制台日志中看到这是真的。 MyService构造函数中的console.log语句在IsRunning()之前输出returns.
有没有其他方法可以做到这一点。我是否可以在 IsRunning() 函数中使用代码手动注入提供者,而不是依赖 @NgModule 中的提供者数组。
APP_INITIALIZER 令牌及其提供服务的工厂在 AppInitModule 中实现:
export function pingFactory(appInitService: AppInitService) {
return () => appInitService.pingServer();
}
@NgModule({
imports: [HttpClientModule],
providers: [
AppInitService,
{ provide: APP_INITIALIZER, useFactory: pingFactory, deps: [AppInitService], multi: true },
],
})
export class AppInitModule {}
这里是 AppInitService 的代码:
@Injectable({providedIn: 'root'})
export class AppInitService {
constructor(private httpClient: HttpClient) {}
static isRunning: boolean | null = null;
static async isWebServerRunning() {
logMsg("isWebServerRunning Enter");
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
// check every 50 milliseconds for change in "isRunning" status
while (AppInitService.isRunning === null) {
logMsg("isWebServerRunning in delay loop");
await delay(50);
}
let isrunning = AppInitService.isRunning;
logMsg("isWebServerRunning Exit -isRunnning = " + String(isrunning));
return isrunning;
}
pingServer(): Promise<any> {
logMsg("pingServer. Enter");
const promise = this.httpClient
.get(server)
.toPromise()
.then((settings) => {
logMsg("pingServer Got server response");
AppInitService.isRunning = true;
return true;
})
.catch((err) => {
logMsg("pingServer No server Response");
AppInitService.isRunning = false;
err;
});
return promise;
}
}
我检查了 stackblitz 示例,发现您在 appinit.module.ts
中有 APP_INITIALIZER,但它必须导入到根模块 (app.module.ts
) 中并重构了一些代码。我为服务创建了一个管理器模块和一个工厂,以便根据我们的条件初始化服务器。
Here my solution on Stackblitz
我添加了一些文件夹,并且在每个文件中都写了解释作为注释。
app.module.ts
// imports omitted...
export function pingFactory(appInitService: AppInitService) {
return () => appInitService.pingServer();
}
@NgModule({
declarations: [AppComponent],
// MyServiceManagerModule must be imported calling forRoot on our root module
// this module internally handle which service will be used
imports: [BrowserModule, HttpClientModule, MyServiceManagerModule.forRoot()],
providers: [
// our APP_INITIALIZER must be imported on our root module too
{
provide: APP_INITIALIZER,
useFactory: pingFactory,
deps: [AppInitService],
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
my-service-manager.module.ts
// imports omitted...
// this factory needs AppInitService to know if our web server is running
// in order to select our service
export function myServiceFactory(
appInitService: AppInitService
): MyService | MyServiceStub {
return appInitService.isRunning ? new MyService() : new MyServiceStub();
}
@NgModule()
export class MyServiceManagerModule {
static forRoot(): ModuleWithProviders<MyServiceManagerModule> {
return {
ngModule: MyServiceManagerModule,
providers: [
// our service factory
{
provide: MyServiceLoader,
useFactory: myServiceFactory,
deps: [AppInitService]
}
]
};
}
}
my-service-loader.ts
// imports omitted...
// our abstract class
@Injectable()
export abstract class MyServiceLoader {
abstract printTime(): void;
abstract getNow(): string;
}
my-service-stub.ts
// imports omitted...
// must extend MyServiceLoader which is an abstract class
@Injectable()
export class MyServiceStub extends MyServiceLoader {
printTime(): void {
let time = this.getNow();
console.log("MyServiceStub:printTime ", time);
}
getNow(): string {
let now = Date.now();
let sec = Math.floor(now / 1000) % 100;
let ms = now % 1000;
return sec.toString() + ":" + ms.toString();
}
}
my-service.ts
// imports omitted...
// must extend MyServiceLoader which is an abstract class
@Injectable()
export class MyService extends MyServiceLoader {
printTime(): void {
let time = this.getNow();
console.log("MyService:printTime ", time);
}
getNow(): string {
let now = Date.now();
let sec = Math.floor(now / 1000) % 100;
let ms = now % 1000;
return sec.toString() + ":" + ms.toString();
}
}
app.component.ts
import { Component } from "@angular/core";
import { MyServiceLoader } from "./my-service/my-service-loader";
@Component({
selector: "app-root",
template: `
<h2>Test for server running</h2>
`
})
export class AppComponent {
title = "testasync";
// MyServiceLoader must be imported
constructor(private myService: MyServiceLoader) {
console.log("AppComponent:ngOnInit", this.getNow());
this.myService.printTime();
}
getNow(): string {
let now = Date.now();
let sec = Math.floor(now / 1000) % 100;
let ms = now % 1000;
return sec.toString() + ":" + ms.toString();
}
}