Page reload fails when using Angular Ui Router with Html5 mode enabled

I am using Angular UI Router in my angular app and i have enabled HTML5 mode to remove the # form the URL by using $locationProvider in the config.

var app = angular.module('openIDC', ['ui.router']);
app.config(function($urlRouterProvider, $stateProvider, $locationProvider) {

    $locationProvider.html5Mode(true);

    $urlRouterProvider.otherwise('/');

    $stateProvider
    .state('home', {
        url: '/',
        templateUrl: 'views/home.html',
        controller: 'HomeController'
    })
    .state('login', {
        url: '/login', 
        templateUrl: 'views/login.html',
        controller: 'LoginController'
    })
});

I have also set the <base href="/" /> tag in the index.html file as well. The routing works fine and i can navigate to pages and the # is removed but when i refresh the page using the reload button on the browser there is a 404 error response.

enter image description here

Why is this happening and how can i fix it and have HTML5 mode enabled to have proper URLs

Answers:

Answer

Kasun, the reason that this is occurring is because you are trying to refresh the page from one of your sub routes (has nothing to do with ui-router).

Basically if you request www.yourdomain.com/ you likely have your server setup to return index.html which bootstraps your angular app. Once the app has loaded, any further url changes take html5Mode into consideration and update your page via ui-router.

When you reload your page the angular app is no longer valid as it has not loaded yet, so if you are trying to load a sub route (for example: www.yourdomain.com/someotherpage), then your server does not know how to deal with /someotherpage and likely returns 404 or some other error.

What you need to do is configure your server to return your angular app for all routes. I primarily use node/express, so I do something like:

app.get('*', function(req, res, next) {
    // call all routes and return the index.html file here
}

Note: I usually use something like this as a final catch all, however I also include other routes above it for things like requesting static files, web crawlers, and any other specific routes that need to be handled.

Answer

You need to to setup url rewrite on server side

for apache it would be something like:

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html

in .htaccess file(be sure that mod_rewrite apache module is enabled)

and config like this for nginx:

location / {
    ....
    try_files $uri $uri/ /index.html =404;
}
Answer

Noticed a comment asking about IIS - didn't see it in here yet - but this is how I have mine working. Make sure to have URL Rewrite 2.0 installed.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="AngularJS Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>`
Answer

first make .htaccess file then copy past these code

<ifModule mod_rewrite.c>
     RewriteEngine on

    # Don't rewrite files or directories
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]


    RewriteRule ^ your root folder/index.html [L]
      </ifModule>


<base url="/"></base> in index.html file
Answer

Cant explain in details why this happening but I can describe you a way i solved out this problem. So i used UrlRewriterFilter for my SpringMvc backend. Angular general module config :

var app = angular.module('ilonaChatGeneralModule', 
    [
        'ui.router'
    ]);

app.config(function($locationProvider){
    $locationProvider.html5Mode(true);
});

app.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider.otherwise('/');

    var home = ['/', {
        url: '/',
        templateUrl: 'views/userlist.html'
    }];

    var login = ['login', {
        url: '/login',
        templateUrl: 'views/login.html'
    }];

    var privateRoom = ['privateroom', {
        url: '/privateroom',
        templateUrl: 'views/privateroom.html'
    }];

    $stateProvider
        .state(home)
        .state(login)
        .state(privateRoom)
        ;
});

My home page, index.html:

<html ng-app="ilonaChatGeneralModule"  ng-controller="ilonaChatGeneralCtrl">
<head>
    <base href="/">
    ...

</head>
<body>
<div>
    <div ui-view=""></div>
</div>
</body>
</html>

This is frontend. For backend I used UrlRewriteFilter, you can get it with following maven dependency:

  <dependency>
        <groupId>org.tuckey</groupId>
        <artifactId>urlrewritefilter</artifactId>
        <version>4.0.3</version>
    </dependency>

I've added it to my SpringMVC WebAppInitializer in next way:

    package com.sbk.config;

    import org.springframework.context.ApplicationContext;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.filter.CharacterEncodingFilter;
    import org.springframework.web.servlet.DispatcherServlet;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;

    import javax.servlet.*;
    import java.nio.charset.StandardCharsets;
    import java.util.EnumSet;

    public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {   
        private static final String URL_REWRITE_FILTER_NAME = "urlRewrite";
        private static final String URL_REWRITE_FILTER_MAPPING = "/*";

...
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            super.onStartup(servletContext);

            FilterRegistration.Dynamic urlReWrite = servletContext.addFilter(URL_REWRITE_FILTER_NAME, new UrlRewriteFilter());
            EnumSet<DispatcherType> urlReWriteDispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
            urlReWrite.addMappingForUrlPatterns(urlReWriteDispatcherTypes, true, URL_REWRITE_FILTER_MAPPING);
        }
    }

This filter requires urlrewrite.xml file under ur WEB-INF directory(seems like in configurable but default place - here). Content of this file:

<!DOCTYPE urlrewrite
        PUBLIC "-//tuckey.org//DTD UrlRewrite 2.6//EN"
        "http://www.tuckey.org/res/dtds/urlrewrite2.6.dtd">

<urlrewrite>

    <rule>
        <from>/login$</from>
        <to>index.html</to>
    </rule>

    <rule>
        <from>/privateroom$</from>
        <to>index.html</to>
    </rule>

</urlrewrite>

I didn't read manuals carefully but i suspect that idea is when you refresh ur browser with http://yourhost:xxxx/privateroom ur app try to find physicaly existing view http://yourhost:xxxx/privateroom. But its absent. And when you redirect it to ur base page angular would built correct link to physical file using its states definition. I can mistake in theory but it works in practice

Answer

If you cannot configure your server, configure your web.xml with the following to handle refresh while the user is on an html5 push path.

<error-page>
    <!-- Mapping a 404 when user refreshes with an html5 push route-->
    <error-code>404</error-code>
    <location>/index.html</location>
</error-page>
Answer

the easiest way worked for me is:

use <base href="/admin/"></base> in index.html

I'm doing it for my admin page

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.