Angular 2 Routing Does Not Work When Deployed to H

2020-02-08 01:09发布

问题:

I am going to develop a simple Angular 2 application. I have created a project with routing, using Angular CLI and added several components to the app using 'ng generate component ' command. Then I specified routing in the app-routing.module.ts as following.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { UserComponent } from './user/user.component';
import { ErrorComponent } from './error/error.component';
import { SpecialpageComponent } from './specialpage/specialpage.component';

const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'about',
    component: AboutComponent
  },
    {
    path: 'user',
    component: UserComponent
  },
  {
    path: 'specialpage',
    component: SpecialpageComponent
  },
  {
    path: '**',
    component: ErrorComponent
  }

];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: []
})
export class AppRoutingModule { }

app.module.ts is as following.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { ErrorComponent } from './error/error.component';
import { UserComponent } from './user/user.component';
import { SpecialpageComponent } from './specialpage/specialpage.component';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    AboutComponent,
    ErrorComponent,
    UserComponent,
    SpecialpageComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

I have not added any modifications for the other components. Then I deployed the application using 'ng serve' command and the app works fine with the links. Eg: http://localhost:4200/about

But when I deploy the project in http-server, the links do not work as expected. I deployed the app using 'http-server ./dist' command and the app gets deployed fine, but the links do not work. When I go to 'http://localhost:4200/about', it gives 404 error.

Am I doing anything wrong? Why 'ng-serve' works and 'http-server' does not work?

Any help would be appreciated. Thanks in advance.

I have uploaded my project to github.

回答1:

This problem is solved by implementing HashLocationStrategy which adds # to all your routes. You achieve this by adding HashLocationStrategy to AppModule providers.

    providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}],

and add the corresponding import

   import { HashLocationStrategy, LocationStrategy } from '@angular/common';

This will solve your problem.

And if you don't want to use the HashLocationStrategy, you can use the PahtLocationStrategy, and so your Angular app will not show Hash in the URL. For more details about it check the official Link: https://angular.io/api/common/PathLocationStrategy



回答2:

This is because http-server does not support fallback like lite-server or web pack-dev server. This is why it shows 404 not found. There are two solution to resolve this issue:

  1. you can use HashLocationStrategy like mentioned above.
  2. If you are deploying it to Apache or IIS server then you can simply add configurations as mentioned here!

Note: For development you can use lite-server.

Hope this will help you.



回答3:

I've resolved this problem with adding this in AppModule Providers:

providers: [{provide: LocationStrategy, useClass: PathLocationStrategy}]

and importing

import { PathLocationStrategy, LocationStrategy } from '@angular/common';

Then I created .htaccess:

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html

Everything works great without # in URLs :)



回答4:

It will be solved in this way:

Case-1: For version less than Angular 5.1

1) Use HashLocationStrategy like mentioned below: In AppModule do the following.

1.1) import { HashLocationStrategy, LocationStrategy } from '@angular/common';

1.2) providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]

Case-2: For version equal or more than Angular 5.1

2.1) Angular has introduced this property onSameUrlNavigation for overcoming refresh issue on the server.

2.2) Add onSameUrlNavigation to the RouterModule.forRoot in imports array .

a) In Application main routing module, i,e app.routing.module.ts / app.routing.ts add the property

See below:

@ngModule({

    imports: 
    [
       RouterModule.forRoot(routes, {onSameUrlNavigation: ‘reload’})
    ],

    exports: [RouterModule],
 });

Hope it help somebody.



回答5:

It will happen because it goes to find a page about which is not there inside at all hence 404. Possible options:

  1. If you want to go with http-server then use a proxy which will redirect everything to http://localhost:your-port.

    Option: -P or --proxy Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com

  2. Don't use express at all. Create your own http-server using express & Redirect everything to index.html

    Assuming public is your folder where u keep all transpiled things.

    var express = require('express');
    var app = express();
    
    var path = __dirname + '/public';
    var port = 8080;
    
    app.use(express.static(path));
    app.get('*', function(req, res) {
        res.sendFile(path + '/index.html');
    });
    app.listen(port);
    


回答6:

If you, like me, want to make no code changes at all, simply use this instead:

https://www.npmjs.com/package/angular-http-server



回答7:

For Apache server:

in the Production servers

Add or create ".htaccess" file into the project root, add a rewrite rule to the .htaccess file as shown

RewriteEngine On
  # If an existing asset or directory is requested go to it as it is
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
  RewriteRule ^ - [L]
  # If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html


回答8:

For Apache Server

  • Create a file name .htaccess
  • Edit that file and write index.html instead of index.php
  • For those who dont have any code, can write the code below in your .htaccess file : RewriteEngine On # If an existing asset or directory is requested go to it as it is RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR] RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d RewriteRule ^ - [L] # If the requested resource doesn't exist, use index.html RewriteRule ^ /index.html

  • This code might not work for SSL certificate - contact your hosting provider OR visit https://httpd.apache.org/docs/current/howto/htaccess.html to refer to docs.



回答9:

As per the documentation at https://angular.io/guide/deployment (Apache, you could also look for nginx) You need to point the server to index.html using the .htaccess file as

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html

RewriteRule ^ /index.html



回答10:

You should try specifying the url in the build from the point the app should initialize:

Ng build --prod --base-href="http://your.url.com/subdirectory/etc"


回答11:

The exact reason why this particular issue is being faced is because of your server settings.

So when you implement the application there are certain steps that you will have to take. One of the essential steps is to mark a particular segment of your path say: http://domain-name/main where main being the path segment must be used as the identifier in your server settings say your server.js or app.js when it comes to a node backend or a webconfig file for IIS and Tomcat deployment.

The reason for marking a particular segment is so that when the server receives any request with that particular segment + additional you reroute it to your application in www or public folder for the angular router to kick in.

This process is known as url rewriting. Hence if the web server or the app server depending on the application size is not under your control then please use hashLocationStratergy else it is always neat to use pathLocationStartergy as it is helpful when it comes to history tracking and other aesthetic and performance benefits.

To read more on this you can visit these links:

For IIS: https://blog.angularindepth.com/deploy-an-angular-application-to-iis-60a0897742e7



回答12:

Add a dot in your base href strings.

Correct

<base href="./home">

Wrong

<base href="/home">

http://www.wisdomofjim.com/blog/solved-problems-with-resource-loading-failures-when-deploying-angular-2-webapps-live



回答13:

I used lite-server.

npm i -D  lite-server

then in my package.json file:

"scripts": {
   "ci:serve:employee-onboarding": "ng build --prod --project=employee-onboarding && lite-server -c lite-server-config.json"
}

to run it: npm run ci:serve:employee-onboarding

my lite-server-config.json file looks like this

{
  "port": 4201,
  "server": { "baseDir": "dist/apps/employee-onboarding" }
}


回答14:

I also faced this issue and found a solution that does not require HashLocationStrategy solution.

Issue is, a server like tomcat looks for a actual folder (for eg : /about ) which does not exist in angular app, as its a single page application. So every request needs to be redirected to index.html.

Easiest way to fix this issue is to add the following java class and override addResourceHandlers() method like below:

@Configuration
class WebMVCCustomConfiguration implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:static").resourceChain(true)
            .addResolver(new PathResourceResolver() {
                @Override
                protected Resource getResource(String resourcePath, Resource location) {
                    String path = "static/";
                    path += (resourcePath.contains(".") && !resourcePath.equals("index.html")) ? resourcePath : "index.html";

                    Resource resource = new ClassPathResource(path);
                    return resource.exists() ? resource : null;
                }
            });
    }
  }

This will fix all the issues and this solution does not exist anywhere.



回答15:

export const routes: Routes =[
   **{ path: '', redirectTo: '/', pathMatch: 'full'},**
     { path: 'about', component: AboutComponent},
     { path: 'user',  component: UserComponent},
     { path: 'specialpage',  component: SpecialpageComponent},
     { path: '**',  component: ErrorComponent}
    ];

Look at the content of blockquote. And then you give the name of "HttpModule" at providers:[] in app.module.ts

Thanks.



回答16:

You can do it while registering your RouterModule.forRoot, you can pass a second object with propert {useHash: true} like the below :

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent} from './app.component';
import {appRoutes} from './app.routes';

@NgModule({
   declarations: [AppComponent],
   imports: [BrowserModule],
   RouterModule.forRoot(appRoutes , {useHash: true})],
   providers: [],
   bootstrap: [AppComponent],
})
export class AppModule {}


回答17:

Change your index.html

<base href="/">
 to 
<base href="./">

Because we are using Tomcat we have mentioned this(.) for Routing is based on this(/) So same like http-server

I mean running normally ng serve you have seen only http://localhost:4200/

but run in server you have put your build into(dist) in webapps

so its come like

http://localhost:8080/dist/

so you need to add <base href="./">

i think this is the problem for you may be solved.



回答18:

in the project folder create .htaccess file an then write

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]

follow this link: https://angular.io/guide/deployment