Рендеринг на стороне сервера Angular 5 с модулем TransferState не работает должным образом

Я использую модуль TransferState для кэширования HTTP-ответа на стороне сервера, но, похоже, он не работает.

Что я сделал, так это добавил ServerTransferStateModule в AppServerModule и BrowserTransferStateModule в AppModule, как предлагается здесь https://next.angular.io/api/platform-browser/TransferState

Затем я добавил ниже перехватчик, который перехватывает каждый HTTP-запрос и:

на сервере выполняет запрос и сохраняет ответ в кеше.

когда на клиенте, проверьте кеш, если кешированный ответ существует, он возвращает его, в противном случае выполняет запрос.

import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformServer } from '@angular/common';
import { TransferState, makeStateKey } from '@angular/platform-browser';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HttpErrorResponse } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import { of } from "rxjs/observable/of";
import { catchError, tap } from 'rxjs/operators';

const CACHE_KEY = makeStateKey('httpCacheKey');

@Injectable()
export class MyInterceptor implements HttpInterceptor {

  private isServer = isPlatformServer(this.platformId);

  constructor(
    @Inject(PLATFORM_ID) private platformId,
    private transferState: TransferState,
    @Inject('BASE_URL') private baseUrl: string) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    let modifiedRequest: any;

    let isLocalRequest: boolean = !req.url.startsWith('http');

    if (isLocalRequest) {

      //skip cache check if the request method isn't GET.
      if (req.method == 'GET') {
        // check and serve cached response, if it exists
        const cachedResponse = this.transferState.get(CACHE_KEY, null as any);

        if (cachedResponse) {
          let modifiedResponse = new HttpResponse<any>({ headers: cachedResponse.headers, body: cachedResponse.body, status: cachedResponse.status, statusText: cachedResponse.statusText, url: cachedResponse.url });
          return of(modifiedResponse);
        }
      }

      // Clone the request to add the new header.
      modifiedRequest = req.clone({ headers: req.headers, url: this.baseUrl + req.url, body: req.body });
    }

    return next.handle(isLocalRequest ? modifiedRequest : req)
      .pipe(tap((event: HttpEvent<any>) => {
        if (event instanceof HttpResponse) {
          if (this.isServer && isLocalRequest) {
            this.transferState.set(CACHE_KEY, event as any);
          }
        }
      }));
  }
}

Я вижу, что ответ кэшируется и передается клиенту, я вижу ниже тег скрипта в разметке, возвращаемой с сервера:

<script id="ng-cli-universal-state" type="application/json">{&q;httpCacheKey&q;:{&q;headers&q;:{&q;normalizedNames&q;:{},&q;lazyUpdate&q;:null},&q;status&q;:200,&q;statusText&q;:&q;OK&q;,&q;url&q;:&q;http://localhost:49805/api/SampleData/WeatherForecasts&q;,&q;ok&q;:true,&q;type&q;:4,&q;body&q;:[{&q;dateFormatted&q;:&q;11/28/2017&q;,&q;temperatureC&q;:28,&q;summary&q;:&q;Mild&q;,&q;temperatureF&q;:82},{&q;dateFormatted&q;:&q;11/29/2017&q;,&q;temperatureC&q;:-15,&q;summary&q;:&q;Balmy&q;,&q;temperatureF&q;:6},{&q;dateFormatted&q;:&q;11/30/2017&q;,&q;temperatureC&q;:51,&q;summary&q;:&q;Freezing&q;,&q;temperatureF&q;:123},{&q;dateFormatted&q;:&q;12/1/2017&q;,&q;temperatureC&q;:45,&q;summary&q;:&q;Mild&q;,&q;temperatureF&q;:112},{&q;dateFormatted&q;:&q;12/2/2017&q;,&q;temperatureC&q;:1,&q;summary&q;:&q;Mild&q;,&q;temperatureF&q;:33}]}}</script>

Но http-запросы выполняются дважды, один раз на сервере и снова в браузере.


person Naveed Ahmed    schedule 28.11.2017    source источник
comment
Просто чтобы убедиться, что вы используете абсолютные URL-адреса на стороне сервера и относительные URL-адреса на стороне клиента?   -  person David    schedule 29.11.2017
comment
Я использую абсолютные URL-адреса как на сервере, так и на клиенте. Состояние передается с сервера на клиент, поэтому он показывает тег сценария, о котором я упоминал выше. Но когда я проверяю кеш, он всегда равен нулю.   -  person Naveed Ahmed    schedule 29.11.2017
comment
Я не уверен, как работают перехватчики (одиночные или один новый на запрос?), но вы всегда используете один и тот же ключ кеша независимо от запрошенного URL-адреса. Может ли это быть проблема?   -  person David    schedule 29.11.2017
comment
Перехватчики являются новыми для каждого запроса, поэтому это не должно быть проблемой. Даже если это синглтон, он должен работать по крайней мере для первого запроса.   -  person Naveed Ahmed    schedule 29.11.2017
comment
У меня такая же проблема. Любое решение еще?   -  person Kevin LeStarge    schedule 05.12.2017


Ответы (1)


Я столкнулся с той же проблемой. Решение найдено в конце это руководство. Вам нужно убедиться, что вы загружаете свой модуль приложения после срабатывания события DOMContentLoaded.

main.ts:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

// Listen for the event before bootstrapping:
document.addEventListener('DOMContentLoaded', () => {
  platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
});
person Kevin LeStarge    schedule 05.12.2017
comment
Большое спасибо, Кевин, ты потрясающий, я боролся с этим с прошлой недели :) - person Naveed Ahmed; 06.12.2017
comment
ребята, я делаю это в своем угловом приложении, но кажется, что событие никогда не происходило, потому что я поместил console.log в функцию прослушивателя событий, и он никогда не срабатывал. есть ли у вас какие-либо идеи или предложения или что-то может помочь? :( - person s.sobati; 01.05.2018