How to convert a subdomain to a path using Embedded Tomcat 8 and Spring boot

How to rewrite subdomains on the way?

Example:

  • foo.bar .example.com → example.com / foo / bar

Or it would be better (reverse folders):

  • foo.bar .example.com → example.com / bar / foo

The request foo.bar .example.com should send the file to / src / main / resources / static / bar / foo /index.html.

With Apache2, this is done by mod_rewrite. I found rewriting documentation with Tomcat 8 , but the question is where to put these files using spring boot?


Update

I tried using UrlRewriteFilter , but it is not possible to define the rules in the domain path by replacing regular expressions.

This is my configuration:

Maven dependency:

<dependency> <groupId>org.tuckey</groupId> <artifactId>urlrewritefilter</artifactId> <version>4.0.3</version> </dependency> 

Spring Java configuration for registering a servlet filter:

 @Configuration @ComponentScan @EnableAutoConfiguration public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new UrlRewriteFilter()); registrationBean.addUrlPatterns("*"); registrationBean.addInitParameter("confReloadCheckInterval", "5"); registrationBean.addInitParameter("logLevel", "DEBUG"); return registrationBean; } } 

urlrewrite.xml in / src / main / webapp / WEB -INF

 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN" "http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd"> <urlrewrite> <rule> <name>Translate</name> <condition name="host" operator="equal">foo.bar.example.com</condition> <from>^(.*)</from> <to type="redirect">example.com/bar/foo</to> </rule> </urlrewrite> 

It works with this hard-coded domain, but it should work for every subdomain like this.

+8
java spring-boot tomcat tomcat8 embedded-tomcat-8
source share
4 answers

Create your own filter.

This filter should:

  • check if you need to rewrite the request.
  • If yes, rewrite the URL and URI
  • request ahead
  • it appears again through the same filter, but the first check will return false
  • don't forget and be careful to call chain.doFilter

Redirects will not change the URLs in the browser. Just send the contents of the file.

The following code could be an implementation of such a filter. This is not some kind of clean code. Just a quick and dirty working code:

 @Component public class SubdomainToReversePathFilter implements Filter { @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { final HttpServletRequest req = (HttpServletRequest) request; final String requestURI = req.getRequestURI(); if (!requestURI.endsWith("/")) { chain.doFilter(request, response); } else { final String servername = req.getServerName(); final Domain domain = getDomain(servername); if (domain.hasSubdomain()) { final HttpServletRequestWrapper wrapped = wrapServerName(req, domain); wrapped.getRequestDispatcher(requestURI + domain.getSubdomainAsPath()).forward(wrapped, response); } else { chain.doFilter(request, response); } } } private Domain getDomain(final String domain) { final String[] domainParts = domain.split("\\."); String mainDomain; String subDomain = null; final int dpLength = domainParts.length; if (dpLength > 2) { mainDomain = domainParts[dpLength - 2] + "." + domainParts[dpLength - 1]; subDomain = reverseDomain(domainParts); } else { mainDomain = domain; } return new Domain(mainDomain, subDomain); } private HttpServletRequestWrapper wrapServerName(final HttpServletRequest req, final Domain domain) { return new HttpServletRequestWrapper(req) { @Override public String getServerName() { return domain.getMaindomain(); } // more changes? getRequesetURL()? ...? }; } private String reverseDomain(final String[] domainParts) { final List<String> subdomainList = Arrays.stream(domainParts, 0, domainParts.length - 2)// .collect(Collectors.toList()); Collections.reverse(subdomainList); return subdomainList.stream().collect(Collectors.joining(".")); } @Override public void init(final FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } } 

Here is the domain class:

 public static class Domain { private final String maindomain; private final String subdomain; public Domain(final String maindomain, final String subdomain) { this.maindomain = maindomain; this.subdomain = subdomain; } public String getMaindomain() { return maindomain; } public String getSubdomain() { return subdomain; } public boolean hasSubdomain() { return subdomain != null; } public String getSubdomainAsPath() { return "/" + subdomain.replaceAll("\\.", "/") + "/"; } } 

And you need a controller that catches everything

 @RestController public class CatchAllController { @RequestMapping("**") public FileSystemResource deliver(final HttpServletRequest request) { final String file = ((String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)); return new FileSystemResource(getStaticFile(file)); } private File getStaticFile(final String path) { try { // TODO handle correct return new File(CatchAllController.class.getResource("/static/" + path + "/index.html").toURI()); } catch (final Exception e) { throw new RuntimeException("not found"); } } } 

I'm not sure if this is necessary to override other methods in the HttpServletRequestWrapper . This reason for comments.

You also need to handle file delivery cases (does not exist, ...).

+7
source share

You can use Backreferences to use grouped parts that match your <condition> . Something like that -

 <condition name="host" operator="equal">(*).(*).example.com</condition> <from>^(.*)</from> <to type="redirect">example.com/%1/%2</to> 

Of course, you will need to adjust the condition rule above to stop impatient matching.

Read more here http://urlrewritefilter.googlecode.com/svn/trunk/src/doc/manual/4.0/index.html#condition

+4
source share

Another way to solve this problem: do it in the controller itself. This, in my opinion, is better than with a filter, because:

  • the filter captures every request. Here you can better control which requests should be delivered using the subdomain template. As an example, I chose /subdomain2path .
  • You do not need to change / protect URIs / URLs and forward them again through the filter chain.
  • All this logic is in this controller.

The getSubdomain and reverseDomain are the same as in the previous answer.

This is impl:

 @RestController @RequestMapping("/subdomain2path") public class Subdomain2PathController { @RequestMapping("/") public FileSystemResource deliver(final HttpServletRequest request) { final Domain subdomain = getSubdomain(request.getServerName()); String file = "/"; if (subdomain.hasSubdomain()) { file = subdomain.getSubdomainAsPath(); } return new FileSystemResource(getStaticFile(file)); } private Domain getSubdomain(final String domain) { final String[] domainParts = domain.split("\\."); String mainDomain; String subDomain = null; final int dpLength = domainParts.length; if (dpLength > 2) { mainDomain = domainParts[dpLength - 2] + "." + domainParts[dpLength - 1]; subDomain = reverseDomain(domainParts); } else { mainDomain = domain; } return new Domain(mainDomain, subDomain); } private String reverseDomain(final String[] domainParts) { final List<String> subdomainList = Arrays.stream(domainParts, 0, domainParts.length - 2)// .collect(Collectors.toList()); Collections.reverse(subdomainList); return subdomainList.stream().collect(Collectors.joining(".")); } private File getStaticFile(final String path) { try { // TODO handle correct return new File(Subdomain2PathController.class.getResource("/static/" + path + "/index.html").toURI()); } catch (final Exception e) { throw new RuntimeException("not found"); } } } 

The domain class is the same as in the answer:

 public static class Domain { private final String maindomain; private final String subdomain; public Domain(final String maindomain, final String subdomain) { this.maindomain = maindomain; this.subdomain = subdomain; } public String getMaindomain() { return maindomain; } public String getSubdomain() { return subdomain; } public boolean hasSubdomain() { return subdomain != null; } public String getSubdomainAsPath() { return "/" + subdomain.replaceAll("\\.", "/") + "/"; } } 
+3
source share

It worked for me. Hope this works for others too.

Please use the following dependency


  <dependency> <groupId>org.tuckey</groupId> <artifactId>urlrewritefilter</artifactId> <version>4.0.4</version> </dependency> 

Urlrewrite.xml file created in resource folder

  <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://www.tuckey.org/res/dtds/urlrewrite3.0.dtd"> <urlrewrite> <rule> <name>Domain Name Check</name> <condition name="host" operator="notequal">www.userdomain.com</condition> <from>^(.*)$</from> <to type="redirect">http://www.userdomain.com$1</to> </rule> 


Added to main ApplicationRunner.java

 @Bean public FilterRegistrationBean tuckeyRegistrationBean() { final FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new CustomURLRewriter()); return registrationBean; } 

And created CustomURLRewriter

 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.core.io.ClassPathResource; import org.tuckey.web.filters.urlrewrite.Conf; import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter; import org.tuckey.web.filters.urlrewrite.UrlRewriter; import javax.servlet.*; import java.io.InputStream; public class CustomURLRewriter extends UrlRewriteFilter { private UrlRewriter urlRewriter; @Autowired Environment env; @Override public void loadUrlRewriter(FilterConfig filterConfig) throws ServletException { try { ClassPathResource classPathResource = new ClassPathResource("urlrewrite.xml"); InputStream inputStream = classPathResource.getInputStream(); Conf conf1 = new Conf(filterConfig.getServletContext(), inputStream, "urlrewrite.xml", ""); urlRewriter = new UrlRewriter(conf1); } catch (Exception e) { throw new ServletException(e); } } @Override public UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) { return urlRewriter; } @Override public void destroyUrlRewriter() { if(urlRewriter != null) urlRewriter.destroy(); } } 
-one
source share

All Articles