-->

Single page application with Java EE/Wildfly: serv

2020-07-18 08:50发布

问题:

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.

回答1:

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']


回答2:

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.