I have a brand new angular universal project that seems to pretender all the HTML (which is good). However, I am trying to make an API call to my .NET server which is a standard API build with the weatherforecast API.
The API calls and it works great, but it only happens after my web app has switched from pre render to csr. see example 1.
Example 1
if I disable javascript on the page this is what I get
and this is the HTML code
<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$);
}
}
app.service.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");
});
I also get what I think is a cors error whenever the prerendering tries to fetch from the API.
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
}
}
but no errors in console:
In startup.cs in my .NET 5 project I have configured cors:
public void ConfigureServices(IServiceCollection services) {
services.AddControllers();
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ModernaMediaDotNet", Version = "v1" });
});
services.AddCors();
}
and in public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseRouting();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true) // allow any origin
);
I once had the same problem on our test environment, and it was due to the fact that we were using a self signed certificate, which node did not like.
We ended up adding the line below in server.ts
so that node disables TLS validation.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
Note This is unsecure (documentation) and you should not use this in prod (where you should have valid certificates).
Another option would be to only make http calls when you are server side, which you could implement using a HttpInterceptor