http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling-custom-error-pages
One of the problems with this however, is if you want to maintain a separate javascript build for your webclient and are using angular and ui-router as a single page webapp, maintaining your sources will become a pain. For instance, if you want menus to continue to work, how are you going to accurately change the state when a 404 page's location is to "/non/existent/weird/path". Without using a template rendering library on the server side, this becomes a problem.
There are a lot of ways this could be handled, you could jump through a lot of hoops rewriting all your urls. You could utilize Spring ResourceTransformers. You could ensure *everything* for headers/footers/and the like are all pulled in as templates (or god forbid copy them and maintain them in two different locations).
Because the project I'm working on already utilized the processhtml grunt plugin, I decided to just add additional targets for each of my error pages.
First, add the targets to your grunt configuration:
// 'paths' is setup earlier in my grunt file | |
processhtml: { | |
mainapp: { | |
options: { | |
strip: true, | |
data: { | |
year: new Date().getFullYear(), | |
localCssPath: paths.minCssName, | |
localJsPath: paths.minJsName | |
} | |
}, | |
files: { | |
'pathToBuildDir/apppath/index.html': ['app/index.html'] | |
} | |
}, | |
error400: { | |
options: { | |
strip: true, | |
data: { | |
year: new Date().getFullYear(), | |
localCssPath: paths.minCssName, | |
localJsPath: paths.minJsName | |
} | |
}, | |
files: { | |
'pathToBuildDir/error/400.html': ['app/index.html'] | |
} | |
}, | |
error404: { | |
options: { | |
strip: true, | |
data: { | |
year: new Date().getFullYear(), | |
localCssPath: paths.minCssName, | |
localJsPath: paths.minJsName | |
} | |
}, | |
files: { | |
'pathToBuildDir/error/400.html': ['app/index.html'] | |
} | |
} | |
} |
Now, let's do some templating within our index.html file like so:
<div class="page-container"> | |
<div class="site-content"> | |
<!-- build:remove:error400,error404 --> | |
<div ui-view="mainBody"></div> | |
<!-- /build --> | |
<!-- build:include:error400 error/400.html --><!-- /build--> | |
<!-- build:include:error404 error/404.html --><!-- /build--> | |
</div> | |
</div> |
This gets us most of the way there. But what if your app is served under a sub directory? What if the 404 is on some nested path.
I tend to dislike using base hrefs (just an opinion), but we can leverage one here. Add the following to the head of your index.html source:
<!-- build:include:error404,error404 error/base.html --><!-- /build--> |
<meta name="yourErrorPageTag" content="true" /> | |
<base href="/apppath/" /> |
What is the meta tag for? Well, that is just something I placed in that can be detected during state change for ui-router. I added the following to my angular app to force changing the window location when a submenu is clicked in my menu bar:
$rootScope.$on('$stateChangeStart', function(evt, to, params){ | |
var isError = $windowProvider.$get().document.querySelector("meta[name='yourErrorPageTag']"); | |
if (isError) { | |
evt.preventDefault(); | |
$windowProvider.$get().location = "/apppath/index.html" + $windowProvider.$get().location.hash; | |
} | |
}); |