Angular2 and Spring Boot. How to serve front-end?

2019-03-20 14:11发布

问题:

I have Spring Boot as back-end and Angular2 as front-end. I want to develop both of them separately and deploy onto Heroku.

They shouldn't have any common dependencies and should be in separate git-repos.

As I understand, there are two main ways to implement:

  1. run npm build and copy dist folder into resource folder of Spring application so last will handle it as a static content

  2. run server for serving exclusively Angular app which will communicate with Spring app (CORS problem appears here?) so there are two servers at sum

I think first way is a bit "dirty" since I do not think that copy folder from one project to another is any good.

And second way is overkill because I have two servers (Tomcat and Node.js, for example). Why should I have server with Angular app if I can simply put Angular inside Spring?

Is there is any more rightful way to make aforementioned?

Thanks.

回答1:

In my organization, we have a lot of Spring Boot and Angular apps. When two servers are unnecessary, Spring Boot can serve up the static content from any supported URL (such as "http:" or "file:"). Simply pass this argument to your app on startup:

--spring.resources.static-locations=<url>

Spring Boot can also support Angular single-page app routing by including the following web MVC configuration. This ensures that even if the user refreshes the page in the browser, Spring Boot will still serve up the contents of index.html for other angular routes.

public class SinglePageAppWebMvcConfigurer extends WebMvcConfigurerAdapter
{
    @Autowired
    private ResourceProperties resourceProperties;

    private String apiPath = "/api";

    public SinglePageAppWebMvcConfigurer()
    {
    }

    public SinglePageAppWebMvcConfigurer(String apiPath)
    {
        this.apiPath = apiPath;
    }

    protected String getApiPath()
    {
        return apiPath;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry)
    {
        registry.addResourceHandler("/**")
            .addResourceLocations(resourceProperties.getStaticLocations())
            .setCachePeriod(resourceProperties.getCachePeriod()).resourceChain(true)
            .addResolver(new SinglePageAppResourceResolver());
    }

    private class SinglePageAppResourceResolver extends PathResourceResolver
    {
        @Override
        protected Resource getResource(String resourcePath, Resource location) throws IOException
        {
            Resource resource = location.createRelative(resourcePath);
            if (resource.exists() && resource.isReadable()) {
                return resource;
            } else if (getApiPath() != null && ("/" + resourcePath).startsWith(getApiPath())) {
                return null;
            } else {
                LoggerFactory.getLogger(getClass()).info("Routing /" + resourcePath + " to /index.html");
                resource = location.createRelative("index.html");
                if (resource.exists() && resource.isReadable()) {
                    return resource;
                } else {
                    return null;
                }
            }
        }
    }
}


回答2:

Option 1 One server process hosting REST APIs and another server process hosting Angular UI

This option is the recommended option in a MicroServices based architecture where the individual APIs (or small related group of APIs) are run and scaled separately by hosting them in separate server processes. In such scenarios, if the Angular UI is also bundled with the REST APIs, it would mean that every bug-fix or enhancement in the Angular UI would require rebuilding and redeployment of your services. And when we start doing that, it would defeat the purpose of Microservices based architecture.

Therefore, in such an architecture one or more server instances would host only the Angular UI. The Angular UI would in turn call the individual APIs through an API Gateway using some service discovery mechanism. However, Microservices based architecture is not trivial - it's complex with a lot of moving parts. Such level of complexity can be justified for large projects.

Option 2 One server process hosting both REST APIs and Angular UI

This option is the recommended option for small to medium sized projects where the user base is a few hundred users.

In such projects, create a single repository where a Maven project would contain two sub-modules - one for the REST APIs and the other for the Angular UI.

Use the Maven plugin "frontend-maven-plugin" to build the UI part. This will automatically download the specified NodeJs version and invoke appropriate npm commands to build the Angular project. Finally, using the Maven element copy the Angular dist folder to the Spring Boot static folder under the resources folder. (In the .gitignore file exclude the static folder so that the Angular distribution files are not checked into the source control along with the REST APIs).

Now, as the Java build will start, it would automatically include the static folder in the fat jar which would now serve both the APIs and the Angular UI.



回答3:

So far I created applications with angular and spring-boot, using one git repository, but two different maven projects, one for the backend, one for the frontend.

With Maven than I built one fat jar with an embedded Tomcat and deployed it to Amazon EC2.

I also experimented with Heroku and you could for sure deploy the same fat jar there.

For the next project I would take another approach and deploy all static resources like html, javascript etc. to Amazon S3 and only the spring-boot app to a provider like heroku.

Frontend deployment this way seems to be much easier, faster and cheaper.

There is also a blog post about Using AWS S3 to Store Static Assets and File Uploads