Angular RouterModule not woking in production

Viewed 2567

I have troubles getting my app working in production environment.

My app uses angular-jwt from @auth0/angular-jwt

If an user has an valid token, he will be forwarded to the MainScreenComponent, elsewhere he will be redirected to the login page.

import { AuthGuard } from './services/Authentication/auth-guard.service';
import { AuthGuardLogin } from './services/Authentication/auth-guard-login.service';
import { AuthService } from './services/Authentication/auth.service';
import { BrowserModule , HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';

import { RouterModule } from '@angular/router';
import { LoginComponent } from './components/login/login.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { NoAccessComponent } from './components/no-access/no-access.component';
import { AppComponent } from './app.component';
import { environment } from '../environments/environment';
import { ServiceWorkerModule } from '@angular/service-worker';
import { MainScreenComponent } from './components/main-screen/main-screen.component';
import { JwtModule } from '@auth0/angular-jwt';

export function tokenGetter() {
  return localStorage.getItem('token');
}

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    NotFoundComponent,
    NoAccessComponent,
    MainScreenComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule,
    ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
    RouterModule.forRoot([
       { path: '', component: MainScreenComponent , canActivate: [AuthGuard]},
       { path: 'login', component: LoginComponent,  canActivate: [AuthGuardLogin] },
       { path: '**', component: NotFoundComponent }
     ]),
     JwtModule.forRoot({
      config: {
        tokenGetter: tokenGetter,
        whitelistedDomains: environment.whiteListDomains,
      }
    })
  ],
  providers: [
  AuthService,
  AuthGuard,
  AuthGuardLogin,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

if I use ng serve everything works like expected, but after ng build --prod, I get an 404 if my initial URL is https://my-site:4300/login. In this case the RouterModule settings will be ignored.

If I type in https://my-site:4300, I will be redirected to https://my-site:4300/login and the login page will be displayed.

Any ideas?

========================

Update 31.10.2018:

Here is the AuthGuardLogin:

import { AuthService } from './auth.service';
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class AuthGuardLogin implements CanActivate {

  constructor( private router: Router, private authService: AuthService) { }

  canActivate() {
    if (this.authService.isLoggedIn()  === false) {
      return true; }

    this.router.navigate(['/']);
    return false;
  }
}

What I have now done, is hosting the website inside an Kestrel Server. Now it works perfect. Why? I don't know :-)!

By the way the Kestrel Server is hosted inside an Windows Service. So I can deploy that with our software package and install it via regular setup.

Best regards and thank you very much!!!

3 Answers

I had a similar problem with a production environment when refresh was bugging out and couldnt identify the page.

I fixed it by using: imports: [RouterModule.forRoot(routes, { useHash: true })],

I am suggesting this solution since you said that when you typed the default url it worked and redirected in login, but when you typed /login it didnt.

useHash is used because angular is a spa and needs to load the main index.html first. In localhost and with webpack it works fine, but when you serve your app through a server you need to go throught the index.html on refresh and when you directly hit a url when not using the angular router.

Your urls will become a little ugly though because a # is going to be added before each url. For example domain/login will become domain/#/login

Hope it helps you

You likely need to configure your server to rewrite unmatched URLs to your index.html.

If you are using IIS,

  1. Select the website under Sites,
  2. under IIS section, double-click on Error Pages
  3. Select 404 Status Code from the list and open in edit mode
  4. Select the radio button "Execute a URL on this site" and under URL textbox write "/" (without double quote)

You need to configure your production web server to always respond with the index.html whenever it detects a 404 - that way, Angular will always load and its routing service will handle the navigation on the client side. Or else you will have to use Hash location strategy.

EDIT: Configuration for other servers:

  1. using live-server: https://www.npmjs.com/package/live-server

    $live-server --entry-file=index.html

  2. using nginx: http://nginx.org/en/docs/beginners_guide.html

    error_page 404 /index.html

  3. Tomcat - configuration of web.xml.

    <error-page> <error-code>404</error-code> <location>/index.html</location> </error-page>

Try adding file src/web.config with the following content:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Angular Routes" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="./index.html" />
        </rule>
      </rules>
    </rewrite>
    <staticContent>
      <remove fileExtension=".woff" />
      <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="font/x-woff" />
      <remove fileExtension=".otf" />
      <mimeMap fileExtension=".otf" mimeType="font/otf" />
    </staticContent>
  </system.webServer>
</configuration>

It works form me as expected. I also added { provide: LocationStrategy, useClass: PathLocationStrategy } to app.module.ts, and <base href="/"> to index.html but not shure that it's relevant.

Related