3

I have a web application that is built as a war using maven. This web application contains my backend as rest service.

I also have and angular application that I want to use to consume said web application (it uses npm and webpack for bundling).

How can I serve my angular application from the same war that contains my backend? Say, in path .../index.html?

Daniel Calderon Mori
  • 5,466
  • 6
  • 26
  • 36
  • The default servlet will serve files out of src/main/webapp (so you can put your index.htm there). That is unless you have remapped that to some other servlet, you haven't mentioned anything at all about all application – Lev Kuznetsov May 06 '16 at 06:34
  • may have been answered at https://stackoverflow.com/questions/38516667/springboot-angular2-how-to-handle-html5-urls – toongeorges May 08 '18 at 07:19

2 Answers2

2

To have my Angular project work in a WAR, I created a Maven project with only 2 files in it (next to the Angular files):

  • pom.xml
  • src/main/java/com/stackoverflow/AngularFilter.java

The content of pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.stackoverflow.example</groupId>
    <artifactId>angular-example</artifactId>
    <version>1.0.0</version>
    <packaging>war</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <warSourceDirectory>${basedir}/src/angular/dist/angular</warSourceDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

In the warSourceDirectory element, I refer to the Angular build output. (The Angular files have to be built with the correct --base-href value.)

The pom.xml file alone is sufficient to have the Angular application working. However, without the AngularFilter.java file, refreshing Angular routes will not work. With the following filter class, also refreshing Angular routes works:

package com.stackoverflow;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter("/*")
public class AngularFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = ((HttpServletRequest) request);
        String contextPath = httpRequest.getContextPath();
        String requestURI = httpRequest.getRequestURI();
        String angularPath = requestURI.substring(contextPath.length());
        if (isAngularRoute(angularPath)) {
            request.getRequestDispatcher("/index.html").forward(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    private boolean isAngularRoute(String angularPath) {
        return
                /* Do not reroute files in the Angular root, such as index.html,
                   main.js, polyfills.js, runtime.js, styles.css and favicon.ico */
                ((angularPath.indexOf('/', 1) != -1) || (angularPath.indexOf('.') == -1))
                /* Do not reroute static files */
            && !angularPath.startsWith("/assets/");
    }

    @Override
    public void destroy() {}
}

There are other solutions that use Spring MVC to enable refreshing Angular routes as explained here: Springboot/Angular2 - How to handle HTML5 urls?

However, I think it is overkill to use Spring if a single Filter class does the job.

toongeorges
  • 1,844
  • 17
  • 14
1

You are talking about serving from a web application, so your angular application should be embedded inside the WAR in order to do that. If your reasons for having 2 projects is because you want to keep concerns separated, then why does the web app need to serve the angular app? I would choose the project design pattern you want and stick to it.

  • Build/package the angular app inside of the REST application. Your servlet can serve up the initial index.html and then handle the remaining requests for your REST API. This allows you to share the same context root for both without any additional intervention.
  • Keep them separate and have a small web application serve up your angular front end (could use node for that too), and then just access your REST backend at the path of the deployed WAR. This means you'd have 2 applications concurrently (1 to serve the angular resources, 1 to serve the REST services), each with their own context root.

Based on the minimal details provided, what you are looking to do can definitely be done using virtually any servlet framework, however considering you have not provided information such as servlet implementation/configuration (ie, Spring? vanilla HttpServlets?), nor have you provided the directory structure of your project, I can't offer much more right now.

alexanderific
  • 780
  • 7
  • 16
  • You didn't answer the question. There is nothing wrong with the way he wants to it. – FelixM Mar 11 '18 at 19:07
  • My first bullet addresses the reasoning and very basic implementation for keeping them together, which is what the vaguely worded question is referring to. My second bullet discusses an alternative depending on his actual needs, which wasn't elaborated on, and was later hinted at by Lev's comment. – alexanderific Mar 27 '18 at 12:36
  • I understand, but I am assuming that running 2 servers is the obvious simple choice. The question is about the more complex approach of running everything from a single WAR. – FelixM Mar 29 '18 at 22:03
  • Which I addressed with the first bullet. It isn't what i'd consider "complex" to package everything into a single WAR, I just had no other intricacies I could offer with the little details in the question. OP didn't provide any details about what framework (if any) they were using, nor did they offer any additional information about the directory structure or configuration of their web/REST apps. I'd be happy to edit my question if I received more information about the project setup, say using standard HttpServlets, or Spring configuration. Until then my answer can't get much more specific. – alexanderific Mar 30 '18 at 14:16
  • 2
    I have an Angular front end. If given the choice, I would run it on an nginx server, use the try_files directive and I am set. The company policy however is that the front end has to be run on a Java server in a WAR file (that is then again packaged in an EAR file). As impractical as the question may look, at times, management makes impractical decisions we have to live with and then it is nice to get answers about how we could best deal with these inconveniences. – toongeorges May 07 '18 at 16:41