5

I want to write an SPA with AngularJS on client side and Java EE on server side. If I understand it correctly, the idea for the frontend is to create a default page (let's call it index.html) and implement routing to exchange parts of this default page. So with every request the default page is loaded and the routing logic replaces its parts depending on context path:

<!-- index.html -->
<html>
    <body>
        <!-- this block is replaced depending on context -->
        <div ng-view></div>
    </body>
</html>

<!-- page one -->
<div>
    <h1>Page One</h1>
    <a href="/specific">Some specific stuff</a>
</div>

<!-- page two -->
<div>
    <h1>Page Two</h1>
</div>

Now, the routing logic could be something like this:

angular.module('myApp', [])
    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider
            .when('/', {templateUrl: 'pages/pageOne.html'})
            .when('/someSpecific', {templateUrl: 'pageTwo.html'})
            .otherwise({redirectTo: '/'});
    }
]);

The question is, how do I couple this with Java EE and a Wildfly server instance? If I declare index.html as the welcome page and do nothing else, the direct calls like http://website.org/someSpecificContext will fail because no page is mapped to the path (and shouldn't be), so no page and no angular code will be loaded. If I make a redirection from every possible subpath to index.html in a servlet filter, then the path information will be lost, so every call will end in the index page. Maybe it's a silly newbie question, but I'm really stuck here and would appreciate any help.

hoefling
  • 59,418
  • 12
  • 147
  • 194
  • you don't redirect, you just always serve the index.html ... think *virtual directories* – charlietfl Oct 14 '15 at 14:16
  • charlieftl, can you please point me on this topic in server terms? Is it the server/the Java EE application that should be preconfigured and how? – hoefling Oct 14 '15 at 14:25
  • i don't know anything about java ee ... google search seems to show lots of results though for this. As it stands right now you aren't using `html5Mode` so all your angular paths will have a `#` in them and won't have any server impact at all that way. You only need to configure server if you use `html5Mode` and use pretty url's without `#`. Serve the templates any way you want – charlietfl Oct 14 '15 at 14:30
  • I'm already using the `html5mode`, I didn't include it for code simplicity. I will update the question to draw less attention to angularJS topic. – hoefling Oct 14 '15 at 15:38

2 Answers2

8

I personally use undertow rewrite handler for this. There is unfortunately not a lot of documentation about this feature. You can find info here and also here. You have to basically rewrite those html 5 urls that only Angular knows about, however you have to respond with your server side REST component (I suppose you're using REST) when Angular asks for backend data. My REST resources are under the /api root-path. This is an example of the undertow-handlers.conf file to be placed in the WEB-INF folder:

regex['(.*/order/?.*?$)'] and not regex['(.*/api.*)'] -> rewrite['/index.html']
regex['(.*/billing/?.*?$)'] and not regex['(.*/api.*)'] -> rewrite['/index.html']
deem
  • 1,252
  • 1
  • 19
  • 38
Franck
  • 1,754
  • 1
  • 13
  • 14
  • Now this is the kind of answer I expected. I knew there should be a possibility of using an existing solution and not reinventing the wheel. Thanks, I will try this out next Monday and report. – hoefling Oct 15 '15 at 13:36
  • Yes, I use REST for transferring data from server to client and ideally, I would implement nothing but REST procedurals on server side with maybe some security logic behind it, but that's all, all the rest should be handled by angular's rich client. This is why I don't like my own solution because it adds unnecessary code to the server and will stick to yours if it works fine. – hoefling Oct 15 '15 at 13:43
  • Your solution has the merit to be portable across Java EE server :-). The undertow handlers however offer a very flexible solution. You have different kind of handlers that you can use for example to set http response headers, status etc. – Franck Oct 15 '15 at 14:01
  • 1
    Here's a shorter rule I came up with that works on Undertow 12.0 and matches all non-api directories: `not file and not path-prefix('api') -> rewrite('/index.html')` – DustInComp Dec 08 '21 at 12:58
0

Ok, this is how I handle it. This is server-independent, the only files you change are in your Java EE project:

  • Put all the files in the appropriate directories:

    / (project's sources root)
    |
     -> js/
     -> css/
     -> webpages/
     -> etc
    
  • Implement a new javax.servlet.Filter:

    public class FilterAccess implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            if (!(request instanceof HttpServletRequest)) {
                return;
            }
            HttpServletRequest req = (HttpServletRequest) request;
    
            if (isAllowed(req)) {
                chain.doFilter(request, response);
            } else { // forward every other request to index page
                req.getRequestDispatcher("/pages/index.html").forward(request, response);
            }
        }
    
        private boolean isAllowed(HttpServletRequest req) {
            List<String> allowed = Arrays.asList("/webpages", "/js", "/css", ...);
            for (String path : allowed) {
                if (req.getServletPath().startsWith(path)) {
                    return true;
                }
            }
            return false;
        }
    }
    
  • Bind it in the web.xml file:

    <filter>
        <filter-name>FilterAccess</filter-name>
        <filter-class>package.for.FilterAccess</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilterAccess</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

Basically, the idea is to forward every request for a resource that does not lie in one of the allowed directories, to the main page. You can also reimplement the isAllowed function as you wish, e.g. checking if the request's URL ends with .js to allow javascript files etc. Also, the code is not very clean, one could declare allowed paths as constants or even move them to an external resource file etc., but you get the point anyway ;-)

Won't accept my own answer yet, though, as I still don't know if this is the correct approach when building a single page application with Java EE.

hoefling
  • 59,418
  • 12
  • 147
  • 194