2

I am developing an application and server which run under Websphere Application Server Liberty profile version 17.0.0.3. I have developed the web application in Angular 4. Everything is working well as long as I don't attempt to refresh/reload a web page. When I try to reload a webpage, I receive a 404 Not Found error. I suspect the problem is that the full url is being passed to the server on refresh. (see Ben Cole's response to this post: What does #/ means in url?)

My problem is that I'm not sure what the best way would be to inject a hashtag into the urls in the application code. I tried simply adding it to the routerLink values in the html, but the url was encoded, converting the hashtag to %23 and then my router didn't recognize the urls.

My Angular 4 application uses Routes and RouterModule from @angular/router. For example, my routes are defined like this:

const routes: Routes = [
{
    path: '', component: LayoutComponent,
    children: [
        { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule' },
        { path: 'versions', loadChildren: './versions/versions.module#VersionsModule' }
  ]
}

And in my html, I navigate to them in this fashion:

<a [routerLink]="['/versions']" 
   [routerLinkActive]="['router-link-active']" class="list-group-item">
                        <i class="fa fa-fw"></i>Versions</a>

My server url is: http://localhost:9080/zss. When I navigate to my dashboard, the url is: http://localhost:9080/zss/dashboard But if I try to refresh this webpage, I get the 404 Not Found error. I think I need to get the Angular application to navigate to urls like: http://localhost:9080/zss/#dashboard but I'm not sure where to tweak the code to do this or how to overcome the uri encoding which occurs.

Andy Guibert
  • 41,446
  • 8
  • 38
  • 61
  • I don't know WAS, but you need to add some kind of rewrite rules on that server so that it handles passing the path to your main app file (index.hml) – David Feb 07 '18 at 20:58

2 Answers2

4

I'm working on a project now that uses Liberty and Angular, and we had the exact same issue. The problem occurs because Angular treats everything as a single page application, and handles all of the URL routing and navigation on its own.

When you refresh your browser, an HTTP get request will be sent to the Liberty server at something like http://localhost:9080/zss/dashboard, which Liberty doesn't know how to handle, since everything beyond the /zss/ path is being handled by Angular.

To resolve this, we included a web.xml (located in the WEB-INF/ folder of your webapp) with the following contents:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <error-page>
        <error-code>404</error-code>
        <location>/</location>
    </error-page>
</web-app>

This fixed the problem because it re-routed all 404 errors to the root of the application, thus allowing Angular to handle the routing.

Andy Guibert
  • 41,446
  • 8
  • 38
  • 61
  • Beautiful! It worked like a charm! Thank you. BTW, do you have a link to any documentation on defining web.xml for a WAS Liberty server? I didn't even know that WAS Liberty used web.xml. – G. Richards Feb 08 '18 at 14:41
  • Web.xml is Java EE standard, so this this file will be portable across any Java EE compliant app server. You can find it documented here: https://docs.oracle.com/cd/E14571_01/web.1111/e13712/web_xml.htm#WBAPP502 – Andy Guibert Feb 08 '18 at 14:53
2

To expand on the accepted answer, Angular takes advantage of HTML5 to edit your browser's history and location to simulate moving between pages even though it looks like only one real "page" to the browser/server.

The difficulty arises when, as that answer says, you refresh the page and the browser issues a request for a resource it thinks it's looking at, which doesn't actually exist. Setting up web.xml to redirect all 404s to the application root allows you to continue using the HTML5 URLs, but if you are unable to support that history API for one reason or another, you can switch the router to use HashLocationStrategy in app-routing.module.ts:

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(routes, { useHash: true })
  ], 

Which changes the URL to include a # after the real path and put the angular route after that (e.g. contextroot/detail/id becomes contextroot/#/detail/id.) This is more compatible, but removes the ability to replace certain angular routes in your app with server-rendered pages. See the relevant Angular documentation.

lwestby
  • 1,209
  • 6
  • 10
  • Thank you. I tried it out and it worked! Since we don't anticipate having any server-rendered pages, I think I prefer to go this route. It seems more efficient than having to hit the server, produce the Not Found error and redirect to the web-app. – G. Richards Feb 08 '18 at 19:33