1
votes

I have a simple angular application with server side rendering. I described ngOnInit of my component, where I call http.get method. But if I set debug on my Rest end-point I saw that this method called twice. Besides at first call I get HttpRequest without credentials, second one - with credentials. Why? And on console through console.log I saw only one call. How can I achive to call this rest only once and with credentials?

app.module.ts

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';

    import { AppComponent } from './app.component';
    import {HttpModule} from "@angular/http";
    import {FormsModule} from "@angular/forms";
    import {HttpClientModule} from "@angular/common/http";

    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule.withServerTransition({appId: 'angular-universal'}),
        FormsModule,
        HttpClientModule,
        HttpModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

app.server.module.ts

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

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

server.ts

    import 'reflect-metadata';
    import 'zone.js/dist/zone-node';
    import { platformServer, renderModuleFactory } from '@angular/platform-server'
    import { enableProdMode } from '@angular/core'
    import { AppServerModuleNgFactory } from '../dist/ngfactory/src/app/app.server.module.ngfactory'
    import * as express from 'express';
    import { readFileSync } from 'fs';
    import { join } from 'path';

    const PORT = 4000;

    enableProdMode();

    const app = express();

    let template = readFileSync(join(__dirname, '..', 'dist', 'index.html')).toString();

    app.engine('html', (_, options, callback) => {
      const opts = { document: template, url: options.req.url };

      renderModuleFactory(AppServerModuleNgFactory, opts)
        .then(html => callback(null, html));
    });

    app.set('view engine', 'html');
    app.set('views', 'src')

    app.get('*.*', express.static(join(__dirname, '..', 'dist')));

    app.get('*', (req, res) => {
      res.render('index', { req,  preboot: true});
    });

    app.listen(PORT, () => {
      console.log(`listening on http://localhost:${PORT}!`);
    });

app.coponent.ts

    import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core';
    import {Hero} from "./hero";
    import {HttpClient} from "@angular/common/http";
    import 'rxjs/add/operator/map';

    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    export class AppComponent implements OnInit{
      title = 'app';
      hero: Hero;
      constructor(private http: HttpClient){
      }

      ngOnInit(): void {
        this.http.get('http://localhost:8080/test', { withCredentials: true }).subscribe(data => {
          console.log("Init component");
          console.log(data);
        });
      }

    }
3

3 Answers

0
votes

Since your API is running on a different server localhost:8000, I'm pretty sure that the CORS (Cross-Origin Resource Sharing) Protocol is used.

The CORS spec requires the OPTIONS call to precede the POST or GET if the POST or GET has any non-simple content or headers in it. It is also called preflight request, see this link for more information.

So if you look at the headers, you most likely will see, that the first call is an OPTIONS call and the second one your actual GET.

To your question: This behavior is by design and is necessary if you're making requests across different origins.

0
votes

If you use angular universal for server side rendering, then it will render the page on server side first (1st GET request), and then again in the browser (2nd GET request).

There is a technique called state transfer, that allows you to "cache" requests made by server, transfer them to your client and reuse the response, so you do not need to make them again. This feature is currently being implemented into angular/universal, but it is fairly easy to implement it by your own (using HttpClient interceptor).

Also you can add conditions to your code, so e.g. you will not make API requests from server side where you know they would fail (e.g. missing authorization).

This is how you can do it:

constructor(@Inject(PLATFORM_ID) private platformId: Object) { ... }

ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
        // Client only code.
    ...
    }
    if (isPlatformServer(this.platformId)) {
        // Server only code.
    ...
    }
}
0
votes

angular server side rendering step by step run below comment

Step 1: ->ng add @ng-toolkit/universal

step 2:->npm install

step 3:->npm run build:prod

step 4:->ng build --prod

step 5:->npm run server

step 6:-> run cmd and write the command ->curl http://localhost:4000

my server side angular application https://exampreparation.study