Dot-Net

Angular Universal 在渲染之前不會等待 api/http 請求

  • March 22, 2021

我有一個全新的 Angular 通用項目,它似乎偽裝了所有的 HTML(這很好)。但是,我正在嘗試對我的 .NET 伺服器進行 API 呼叫,該伺服器是使用 weatherforecast API 建構的標準 API。

API呼叫並且效果很好,但它只發生在我的網路應用程序從預渲染切換到csr之後。見範例 1。

範例 1

在此處輸入圖像描述

如果我在頁面上禁用 javascript 這就是我得到的

在此處輸入圖像描述

這是HTML程式碼

 <div style="padding: 5rem">
   <h1>TEST</h1>
   <h2>{{ this.SampleMessage }}</h2>
   <div *ngFor="let product of weather$ | async">
     <p>{{ this.product.summary }}</p>
   </div>
 </div>

app.component.ts

import { AppService } from './app.service';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
 title = 'ModernaMediaAngular';
 text:string = "test"

 weather$: Observable<any>;
 SampleMessage="Example of Angular Async Pipe";    
 
 constructor(private as: AppService, ) {}

 async ngOnInit() {
   await this.getWeatherAsyncPipe();
   //non async
   this.as.getWeather().subscribe( res =>
     {
       this.text = res[0].date;
       console.log("got resolution");
       console.log(res);
       console.log(this.text);
     }
   );


 }
 public async getWeatherAsyncPipe() {    
       this.SampleMessage="Example of Angular Async Pipe";    
       this.weather$ = await this.as.getWeatherAsync();    
       console.log(this.weather$);
     }    
}

應用服務.ts

import { environment } from './../environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
 providedIn: 'root'
})
export class AppService {
 public waeatherUrl = environment.url + '/api/weatherForecast/get'
 constructor(private http: HttpClient) { }

 getWeather() : Observable<object> {
   const headers = new HttpHeaders(
     {'Content-Type': 'application/json',
     'Access-Control-Allow-Origin' : 'http://localhost:4200'
   }
     );
   var x = this.http.get(this.waeatherUrl, {headers: headers}).pipe();
   console.log(x);
   return x;
 }

 public getWeatherAsync():Observable<any> {
   return this.http.get<any[]>(this.waeatherUrl);    
 }
}

app.module.ts

import { BrowserStateInterceptor } from './interceptors/browserstate.interceptor';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';

import { TransferHttpCacheModule } from '@nguniversal/common'

@NgModule({
 declarations: [
   AppComponent
 ],
 imports: [
   BrowserModule.withServerTransition({ appId: 'serverApp' }),
   TransferHttpCacheModule,
   AppRoutingModule,
   HttpClientModule,
 ],
 bootstrap: [AppComponent]
})
export class AppModule {}

app.server.module

import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
 imports: [
   AppModule,
   ServerModule,
   ServerTransferStateModule
 ],
 bootstrap: [AppComponent],
})
export class AppServerModule {}

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();
}

document.addEventListener('DOMContentLoaded', () => {
 platformBrowserDynamic().bootstrapModule(AppModule)
 .catch(err => console.error(err));
 console.log("DOMCONTENTLOADED");
});

每當預渲染嘗試從 API 中獲取時,我也會得到我認為的 cors 錯誤。

chunk {main} main.js, main.js.map (main) 66.3 kB [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 141 kB [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.15 kB [entry] [rendered]
chunk {styles} styles.css, styles.css.map (styles) 118 bytes [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 2.8 MB [initial] [rendered]
Date: 2021-03-15T14:26:38.903Z - Hash: a20337e49cf8941fcbf6 - Time: 12492ms
Hash: af451acd21594faf190e
Time: 19059ms
Built at: 2021-03-15 15:26:41
     Asset      Size  Chunks                          Chunk Names
   main.js  6.44 MiB    main  [emitted]        [big]  main
main.js.map  6.99 MiB    main  [emitted] [dev]         main
Entrypoint main [big] = main.js main.js.map
chunk {main} main.js, main.js.map (main) 6.08 MiB [entry] [rendered]

Compiled successfully.
** Angular Universal Live Development Server is listening on http://localhost:4200, open your browser on http://localhost:4200 **
Observable {
 _isScalar: false,
 source: Observable {
   _isScalar: false,
   source: Observable {
     _isScalar: false,
     source: [Observable],
     operator: [MergeMapOperator]
   },
   operator: FilterOperator { predicate: [Function], thisArg: undefined }
 },
 operator: MapOperator { project: [Function], thisArg: undefined }
}

Observable {
 _isScalar: false,
 source: Observable {
   _isScalar: false,
   source: Observable {
     _isScalar: false,
     source: [Observable],
     operator: [MergeMapOperator]
   },
   operator: FilterOperator { predicate: [Function], thisArg: undefined }
 },
 operator: MapOperator { project: [Function], thisArg: undefined }
}

ERROR HttpErrorResponse {
 headers: HttpHeaders {
   normalizedNames: Map {},
   lazyUpdate: null,
   headers: Map {}
 },
 status: 0,
 statusText: 'Unknown Error',
 url: 'https://localhost:5001/api/weatherForecast/get',
 ok: false,
 name: 'HttpErrorResponse',
 message: 'Http failure response for https://localhost:5001/api/weatherForecast/get: 0 Unknown Error',
 error: ProgressEvent {
   type: 'error',
   target: XMLHttpRequest {
     onloadstart: null,
     onprogress: null,
     onabort: null,
     onerror: null,
     onload: null,
     ontimeout: null,
     onloadend: null,
     _listeners: [Object],
     onreadystatechange: null,
     _anonymous: undefined,
     readyState: 4,
     response: null,
     responseText: '',
     responseType: 'text',
     responseURL: '',
     status: 0,
     statusText: '',
     timeout: 0,
     upload: [XMLHttpRequestUpload],
     _method: 'GET',
     _url: [Url],
     _sync: false,
     _headers: [Object],
     _loweredHeaders: [Object],
     _mimeOverride: null,
     _request: null,
     _response: null,
     _responseParts: null,
     _responseHeaders: null,
     _aborting: null,
     _error: null,
     _loadedBytes: 0,
     _totalBytes: 0,
     _lengthComputable: false
   },
   currentTarget: XMLHttpRequest {
     onloadstart: null,
     onprogress: null,
     onabort: null,
     onerror: null,
     onload: null,
     ontimeout: null,
     onloadend: null,
     _listeners: [Object],
     onreadystatechange: null,
     _anonymous: undefined,
     readyState: 4,
     response: null,
     responseText: '',
     responseType: 'text',
     responseURL: '',
     status: 0,
     statusText: '',
     timeout: 0,
     upload: [XMLHttpRequestUpload],
     _method: 'GET',
     _url: [Url],
     _sync: false,
     _headers: [Object],
     _loweredHeaders: [Object],
     _mimeOverride: null,
     _request: null,
     _response: null,
     _responseParts: null,
     _responseHeaders: null,
     _aborting: null,
     _error: null,
     _loadedBytes: 0,
     _totalBytes: 0,
     _lengthComputable: false
   },
   lengthComputable: false,
   loaded: 0,
   total: 0
 }
}

ERROR HttpErrorResponse {
 headers: HttpHeaders {
   normalizedNames: Map {},
   lazyUpdate: null,
   headers: Map {}
 },
 status: 0,
 statusText: 'Unknown Error',
 url: 'https://localhost:5001/api/weatherForecast/get',
 ok: false,
 name: 'HttpErrorResponse',
 message: 'Http failure response for https://localhost:5001/api/weatherForecast/get: 0 Unknown Error',
 error: ProgressEvent {
   type: 'error',
   target: XMLHttpRequest {
     onloadstart: null,
     onprogress: null,
     onabort: null,
     onerror: null,
     onload: null,
     ontimeout: null,
     onloadend: null,
     _listeners: [Object],
     onreadystatechange: null,
     _anonymous: undefined,
     readyState: 4,
     response: null,
     responseText: '',
     responseType: 'text',
     responseURL: '',
     status: 0,
     statusText: '',
     timeout: 0,
     upload: [XMLHttpRequestUpload],
     _method: 'GET',
     _url: [Url],
     _sync: false,
     _headers: [Object],
     _loweredHeaders: [Object],
     _mimeOverride: null,
     _request: null,
     _response: null,
     _responseParts: null,
     _responseHeaders: null,
     _aborting: null,
     _error: null,
     _loadedBytes: 0,
     _totalBytes: 0,
     _lengthComputable: false
   },
   currentTarget: XMLHttpRequest {
     onloadstart: null,
     onprogress: null,
     onabort: null,
     onerror: null,
     onload: null,
     ontimeout: null,
     onloadend: null,
     _listeners: [Object],
     onreadystatechange: null,
     _anonymous: undefined,
     readyState: 4,
     response: null,
     responseText: '',
     responseType: 'text',
     responseURL: '',
     status: 0,
     statusText: '',
     timeout: 0,
     upload: [XMLHttpRequestUpload],
     _method: 'GET',
     _url: [Url],
     _sync: false,
     _headers: [Object],
     _loweredHeaders: [Object],
     _mimeOverride: null,
     _request: null,
     _response: null,
     _responseParts: null,
     _responseHeaders: null,
     _aborting: null,
     _error: null,
     _loadedBytes: 0,
     _totalBytes: 0,
     _lengthComputable: false
   },
   lengthComputable: false,
   loaded: 0,
   total: 0
 }
}

但控制台中沒有錯誤:

在此處輸入圖像描述

在我的 .NET 5 項目的startup.cs中,我配置了 cors:

public void ConfigureServices(IServiceCollection services) {
   services.AddControllers();
   services.AddSwaggerGen(c => {
       c.SwaggerDoc("v1", new OpenApiInfo { Title = "ModernaMediaDotNet", Version = "v1" });
       });
   services.AddCors();
}

並在公共 void Configure(IApplicationBuilder app, IWebHostEnvironment env)

app.UseRouting();
app.UseCors(x => x
           .AllowAnyOrigin()
           .AllowAnyMethod()
           .AllowAnyHeader()
           .SetIsOriginAllowed(origin => true) // allow any origin
           );

我曾經在我們的測試環境中遇到過同樣的問題,這是由於我們使用的是自簽名證書,這是節點不喜歡的。

我們最終添加了下面的行,server.ts以便節點禁用 TLS 驗證。

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

注意這是不安全的(文件),你不應該在產品中使用它(你應該有有效的證書)。

另一種選擇是僅在伺服器端進行 http 呼叫,您可以使用HttpInterceptor

引用自:https://stackoverflow.com/questions/66639936