How to rewrite subdomains to paths?
Example:
Or better would be (reverse folders):
Requesting foo.bar .example.com should ship a file in /src/main/resources/static/ bar/foo /index.html.
With Apache2 it is done by mod_rewrite. I found documentation about rewriting with Tomcat 8 but the question is where to put this files using spring boot?
Update
I tried using the UrlRewriteFilter, but it seems not possible to define rules in the domain path using regexes substitution.
This is my configuration:
Maven Dependency:
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.3</version>
</dependency>
Spring Java Config to register the 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>
With this hardcoded domain it works, but it should work for every subdomain like this.
Create you own Filter.
This Filter should:
chain.doFilter
Forwarding will not change any URLs in browser. Just ship content of file.
Follow code could be implementation of such filter. This is not any kind of clean code. Just 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 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 HttpServletRequestWrapper
. That's reason for comment.
Also you have to handle cases for file delivery (not existent, ...).
You can use Backreferences
to use grouped parts that matched in your <condition>
. Something like this -
<condition name="host" operator="equal">(*).(*).example.com</condition>
<from>^(.*)</from>
<to type="redirect">example.com/%1/%2</to>
Of course, you'll have to tweak the condition rule above to stop eager matching.
More here - http://urlrewritefilter.googlecode.com/svn/trunk/src/doc/manual/4.0/index.html#condition
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With