1

I have a spring boot application, in the properties file I have defined:

server.servlet.context-path= /myapp

If I go to http://localhost/myapp I see everything as expected. However if I go to: http://localhost I see a standard 404 error page from Apache Tomcat.

How can I setup a redirect from / to /myapp in spring boot?

Alternatively / equivalently: Can I setup multiple context-paths in a single spring-boot application?

Matthew
  • 10,361
  • 5
  • 42
  • 54
  • Why don't you just create `@Controller` mapped to the `/` root, which redirects to your context-path? – Giorgi Tsiklauri Aug 10 '20 at 12:58
  • @GiorgiTsiklauri - context-path trumps the path specified by a controller's `@RequestMapping`, so if I the controller to `/` it prefixes that with the context path and binds to `/myapp/`. – Matthew Aug 10 '20 at 13:02
  • 2
    @Matthew even if the absolute path is provided? – Giorgi Tsiklauri Aug 10 '20 at 13:02
  • 1
    If you are using Tomcat as the servlet engine you can probably use a rewrite rule to forward requests from / to the target path. Something like this: https://stackoverflow.com/questions/39493444/spring-boot-tomcat-rewritevalve – stringy05 Aug 16 '20 at 23:20
  • You mean when you launch the application, it should open `localhost:port/myapp` ?. – Amit Mishra Aug 17 '20 at 09:52

3 Answers3

5

Your question will result in bad referenced app later because you are arrived on a webname and you are landing everytime on a redirect... Bad way to go. If I were you, I will put the things correctly at the start : create a root virtualhost inside your Tomcat and serve your application with root "/" context, and not "/myapp", unless you (or your client, boss) want it like that...

Example config from a VirtualHost with Apache Tomcat : From https://tecadmin.net/create-virtualhost-in-tomcat/

<Host name="example.com"  appBase="webapps" unpackWARs="true" autoDeploy="true">
 <Alias>www.example.com</Alias>
 
 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="example_access_log" suffix=".txt"
           pattern="%h %l %u %t %r %s %b" />
 
 <Context path="" docBase="/opt/tomcat/webapps/myapp1" debug="0" reloadable="true"/>
</Host>

<Host name="mydomain.org"  appBase="webapps" unpackWARs="true" autoDeploy="true">
 <Alias>www.mydomain.org</Alias>
 
 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="mydomain_access_log" suffix=".txt"
           pattern="%h %l %u %t %r %s %b" />
 
 <Context path="" docBase="/opt/tomcat/webapps/myapp2"
    debug="0" reloadable="true"/>
</Host>
BendaThierry.com
  • 2,080
  • 1
  • 15
  • 17
  • The confusing thing for me is that I am 'just' a Spring Boot user, not a tomcat user. I understand that tomcat is there, but ideally it's not something I want to care about. Is there a Spring Boot way of having multiple contexts in a single app? (I'm guessing the answer is "no") – Matthew Aug 17 '20 at 08:35
  • Of course there is a way to define multiple contexts inside your Spring application. https://stackoverflow.com/questions/54308472/multi-context-spring-boot-application-how-to-define-standard-spring-boot-proper or https://www.baeldung.com/spring-boot-context-hierarchy – BendaThierry.com Aug 17 '20 at 10:40
  • You can look at that GitHub.com repository too https://github.com/kulabun/spring-boot-multi-context – BendaThierry.com Aug 17 '20 at 10:43
  • OK - I tried multiple contexts, and it works. But they share a global context-path. Can you point me to an example where there are two contexts with different context paths? – Matthew Aug 17 '20 at 13:38
  • You can imagine the same configuration for Tomcat I have provided in my answer, inside the same Tomcat VirtualHost and not in twice ``. The interesting parts are `` and another one with `` – BendaThierry.com Aug 17 '20 at 14:24
  • 1
    If you are trying to define multiple root contexts, then it will not work properly : one tomcat context is one application which can handle multiple subcontexts ie with your spring application. It could work with some hacking, but it will be a mess to manage in a daily working workflow. You must define a servlet which handles a specify path in your app 1 and another servlet which handles another specific path in your app 2 and define them inside your tomcat / wildfly root context path, and then it is the same than defining multiple sub contexts for one web app. And it is the simplest one. – BendaThierry.com Aug 17 '20 at 14:44
2

If you package SpringBoot application as jar, Tomcat would be included as the default embedded container.

You can't ask the embedded Tomcat to host multi web-app like what it could do as a standalone service.

So we have two choice:

  1. Run a reverse proxy in front of your SpringBoot application. Such as Nginx

  2. Package your SpringBoot application as a war, and put it into some container.

    For example:

    1. Download tomcat and start it with default configuration.
    2. Build a my-app-1.0.0-SNAPSHOT.war, rename it to myapp.war and copy it to your tomcat's /webapps directory.
    3. You can visit http://localhost:8080/myapp, tomcat could host all valid folders and wars in /webapps.

PS: If you are using Spring Reactive Web(WebFlux, Netty), the second method wouldn't work.

UPDATE

Here is what I said about ServletRegistrationBean in comments.

  // use DispatcherServlet here
  private ServletRegistrationBean<? extends Servlet> createServletRegistrationBean(
      ApplicationContext context, String name, String... urlMappings) {
    final DispatcherServlet dispatcherServlet = new DispatcherServlet();
    dispatcherServlet.setApplicationContext(context);

    final ServletRegistrationBean<DispatcherServlet> servletRegistrationBean =
        new ServletRegistrationBean<>(dispatcherServlet, urlMappings);
    servletRegistrationBean.setName(name);
    return servletRegistrationBean;
  }

  @Bean
  public ServletRegistrationBean<? extends Servlet> oneContextPath(ApplicationContext context) {
    // create applicationContext or use the auto configured one
    return createServletRegistrationBean(context, "firstOne", "/*");
  }
  @Bean
  public ServletRegistrationBean<? extends Servlet> anotherContextPath(ApplicationContext context) {
    return createServletRegistrationBean(context, "secondOne", "/myapp/*");
  }

As this example, we can run http GET /foo and http GET /myapp/foo at the same time.

Notes:

  1. WebFlux is NOT supported.
  2. Custom applicationContext if you need.
Tsing
  • 119
  • 6
  • If you use Maven, you can specify inside the `` section, a `myapp.war`. – BendaThierry.com Aug 16 '20 at 18:27
  • I guess what I'm trying to understand is *why* does embedded tomcat only support a single application / context? But I'm willing to accept that it's one-of-those-things. – Matthew Aug 17 '20 at 08:32
  • 1
    A good question. After some search, I find that I'm wrong. We can't set multi context in properties, but we can do it by: custom and register multi `ServletRegistrationBean` for every web context you want. – Tsing Aug 17 '20 at 09:22
  • How do you figure this stuff out? Is it documented anywhere? – Matthew Aug 17 '20 at 10:22
  • The multiple `ServletRegistrationBean`s works - but it's on top of the path - so the resulting URL is `/context-path/myapp/`. It's a solution to the problem, but using a different mechanism. – Matthew Aug 17 '20 at 13:46
0

You have so many options to do that and for external servers by WebServerFactoryCustomizer

@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
  webServerFactoryCustomizer() {
    return factory -> factory.setContextPath("/context");
}

With Spring Boot 1, we can create an instance of EmbeddedServletContainerCustomizer:

@Bean
public EmbeddedServletContainerCustomizer
  embeddedServletContainerCustomizer() {
    return container -> container.setContextPath("/yourContext");
}

Or with java System property

public static void main(String[] args) {
    System.setProperty("server.servlet.context-path", "/yourContext");
    SpringApplication.run(Application.class, args);
}
Amit Mishra
  • 498
  • 5
  • 16
  • These allow me to change the context-path - but they don't allow me to have multiple contexts paths active. – Matthew Aug 17 '20 at 10:20
  • @Matthew, I don't think that you can achieve this in static way, you will need to have dynamic context path configuration to have more than. I think [this link should help you](https://www.broadleafcommerce.com/blog/configuring-a-dynamic-context-path-in-spring-boot) – Amit Mishra Aug 17 '20 at 10:30
  • I did see that link - it doesn't really make the context path dynamic. What it does is simulate a context path using wrapping / filtering. Though I appreciate this is a subtle distinction - and that what I'm looking for might not be possible! – Matthew Aug 17 '20 at 12:19