nguniversal экспресс-запрос двигателя нуль - PullRequest
0 голосов
/ 21 января 2019

Я успешно запускаю универсальное угловое приложение с @nguniversal/express-engine.Часть, которую я не могу получить, это внедрение объекта node / express req в угловое приложение.Следование официальной документации не решает проблему для меня.Ошибка:

NullInjectorError: Нет провайдера для InjectionToken REQUEST!

На сервере я настраиваю экспресс-движок со следующим отрывком

app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory, // Give it a module to bootstrap,
  providers: [ provideModuleMap(LAZY_MODULE_MAP) ]
}));
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));

и отобразить индексный файл с помощью (1)

app.get('*', (req: Request, res) => {
  res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req });
});

Инжекция в компонент приложения выглядит как

constructor (
  @Inject(REQUEST) private request: Request
) {}

, а REQUEST импортируется из @ nguniversal/ экспресс-двигатель / маркеры * * +1022.Я также протестировал добавление следующей записи в части рендеринга (1), но она также не работает.

providers: [ { provide: REQUEST, useValue: req } ]

Также попытался ввести с помощью декоратора @Optional() и использовать PLATFORM_ID чтобы проверить, является ли платформа server, объект запроса не внедряется и null.Есть ли у вас какие-либо предложения о том, как успешно получить доступ к объекту req in angular?

1 Ответ

0 голосов
/ 21 января 2019

Вы не можете получить доступ к REQUEST в браузере, это объект на стороне сервера, переданный из expressjs в ng-universal.Так что в худшем случае ваш код должен сделать что-то вроде

constructor(
  @Inject(PLATFORM_ID) private platformId,
  @Optional() @Inject(REQUEST) private request
) {
    doSomethingWithRequestIfServer();
}

someOtherMethod() {
    doSomethingWithRequestIfServer();
}

doSomethingWithRequestIfServer() {
    if (isPlatformServer(this.platformId)) {
        // should see this in stdout of node process, or wherever node logs
        console.log('rendering server side for request:', req);
        /* use req */
    } else {
        // browser console should print null
        console.log('working browser side, request should be null', req);
    }
}    

Тогда, когда в браузере ваше приложение не будет смотреть на Request, тогда как при рендеринге на стороне сервера, будет.То есть, ваш серверный рендеринг будет запускать ваше угловое приложение один раз , в результате этого HTML-код будет отправлен в браузер.Затем браузер снова загрузит приложение Angular и заберет , откуда завершится рендеринг сервера.

В лучшем случае, чтобы избежать if s и проверок платформы, я бы предложил расположить ваш макетприложение, так что ваш код на стороне сервера предоставляется только на стороне сервера, а код на стороне браузера предоставляется в браузере.Это определенно потребует больше кода, но будет более прямым с точки зрения обслуживания.(быстрый ввод текста обезьяны ниже)

Общее объявление интерфейса

// common.ts
export abstract class MyServiceBase {
  abstract doSomething(): void;
}

// if implementations of service will be provided only in Angular implementation, InjectionToken can be used.
export const MY_SERVICE = new InjectionToken<MyServiceBase>('MY_SERVICE');
// otherwise, if server side implementation will be injected by from node process, then should be string only. (For alternative illustrated below in the end).
// export const MY_SERVICE = 'MY_SERVICE';

Общий компонент приложения, который должен отображаться как на сервере, так и в браузере.

// app.component.ts
@Component({
  // skipped
})
export class AppComponent {
  constructor(@Inject(MY_SERVICE) myService: MyServiceBase) {
    myService.doSomething();
  }
}

Общий код приложения, которыйприменимо как для сервера, так и для браузера.

// app.module.ts
@NgModule({
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

// app-browser.service.ts
@Injectable()
export class MyServiceForBrowser extends MyServiceBase {
  constructor() {
    console.log('MyService browser implementation');
  }

  doSomthing(): void {
    // do something meaningful in browser 
  }
}

Загрузите это вместо стандартного AppModule для отделения обеспечения реализации.

// app-browser.module.ts
@NgModule({
  imports: [AppModule],
  providers: [{provide: MY_SERVICE, useClass: MyServiceForBrowser}],
  bootstrap: [AppComponent]
})
export class AppBrowserModule {
}

Серверная часть от углового приложения Если вы можете сразу реализовать функции на стороне сервера в приложении Angular, тогда.

// app-server.service.ts
@Injectable()
export class MyServiceForServer extends MyServiceBase {
  constructor(@Inject(REQUEST) private request) {
    console.log('MyService server implementation');
  }

  doSomthing(): void {
    // do something meaningful on server
    console.log('request is', this.request);
  }
}

Модуль на стороне сервера для начальной загрузки с ngExpressEngine.

// app-server.module.ts
@NgModule({
  imports: [AppModule],
  providers: [{provide: MY_SERVICE, useClass: MyServiceForServer}],
  bootstrap: [AppComponent]
})
export class AppServerModule {}

Альтернативный серверсторона от сервера

В качестве альтернативы, MyServiceForServer может быть даже предоставлено из кода сервера.В этом случае нет необходимости предоставлять реализацию в угловой реализации:

// app-server.module.ts
@NgModule({
  imports: [AppModule],
  bootstrap: [AppComponent]
})
export class AppServerModule {}

Вместо этого запишите его как обычный код на стороне сервера:

export class MyServiceForServer extends MyService {
  constructor(private request) {
    console.log('MyService server implementation');
  }

  doSomthing(): void {
    // do something meaningful on server
    console.log('request is', this.request);
  }
}

И введите как external значение:

app.get('*', (req: Request, res) => {
  // construct the server side service instance
  const myService = new MyServiceForServer(req);
  // render server side with service instance provided
  res.render(join(DIST_FOLDER, 'browser', 'index.html'), {
    providers: [{provide: MY_SERVICE, useValue: myService}],
    req
  });
});

В этом случае убедитесь, что ваш токен MY_SERVICE представляет собой простую строку, а не экземпляр InjectionToken.

...