如何使用 Embedded Tomcat 8 和 Spring boot 将子域转换为路径
Posted
技术标签:
【中文标题】如何使用 Embedded Tomcat 8 和 Spring boot 将子域转换为路径【英文标题】:How to convert a subdomain to a path with Embedded Tomcat 8 and Spring boot 【发布时间】:2015-02-03 18:34:07 【问题描述】:如何将子域重写为路径?
例子:
foo.bar .example.com --> example.com /foo/bar或者更好的是(反向文件夹):
foo.bar .example.com --> example.com /bar/foo请求 foo.bar .example.com 应该在 /src/main/resources/static/ bar/foo /index.html 中发送一个文件。
对于 Apache2,它由 mod_rewrite 完成。 我找到了有关使用Tomcat 8 重写的文档,但问题是使用 spring boot 将这些文件放在哪里?
更新
我尝试使用UrlRewriteFilter,但似乎无法使用正则表达式替换在域路径中定义规则。
这是我的配置:
Maven 依赖:
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.3</version>
</dependency>
Spring Java Config 来注册 Servlet 过滤器:
@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 在 /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>
使用这个硬编码域它可以工作,但它应该适用于像这样的每个子域。
【问题讨论】:
【参考方案1】:创建您自己的过滤器。
这个过滤器应该:
检查是否必须重写请求 如果是,重写 URL 和 URI 转发请求 它再次通过相同的过滤器,但第一次检查将提供错误 别忘了小心拨打chain.doFilter
转发不会改变浏览器中的任何 URL。只需发送文件内容即可。
以下代码可能是此类过滤器的实现。这不是任何一种干净的代码。只是快速而肮脏的工作代码:
@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()
这是域类:
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("\\.", "/") + "/";
你需要一个能捕捉一切的控制器
@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");
我不确定这是否需要覆盖HttpServletRequestWrapper
中的其他方法。这就是评论的原因。
您还必须处理文件传递的情况(不存在,...)。
【讨论】:
【参考方案2】:您可以使用Backreferences
来使用与<condition>
匹配的分组部分。像这样 -
<condition name="host" operator="equal">(*).(*).example.com</condition>
<from>^(.*)</from>
<to type="redirect">example.com/%1/%2</to>
当然,您必须调整上面的条件规则以停止急切匹配。
更多信息在这里 - http://urlrewritefilter.googlecode.com/svn/trunk/src/doc/manual/4.0/index.html#condition
【讨论】:
在这种情况下,您具有包含两部分的硬编码结构。这:mr.foo.bar.example.com -> example.com/bar/foo/mr/
将失败。无论如何都要为简短而简单的解决方案投票:)【参考方案3】:
解决此问题的另一种方法:在 Controller 本身中执行。在我看来,这比使用过滤器更好,因为:
过滤器捕获每个请求。在这里,您可以更好地控制应使用“子域模式”传递哪些请求。例如,我选择了/subdomain2path
。
您不必更改/包装 URI/URL 并再次通过过滤器链转发。
所有逻辑都在这个控制器中
getSubdomain
和 reverseDomain
方法与之前的答案相同。
这是实现:
@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");
域类与之前的答案相同:
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("\\.", "/") + "/";
【讨论】:
【参考方案4】:它对我有用。希望它也适用于其他人。
请使用以下依赖
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.4</version>
</dependency>
在资源文件夹中创建 urlrewrite.xml
<?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>
在主ApplicationRunner.java中添加
@Bean
public FilterRegistrationBean tuckeyRegistrationBean()
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CustomURLRewriter());
return registrationBean;
并创建了一个 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();
【讨论】:
以上是关于如何使用 Embedded Tomcat 8 和 Spring boot 将子域转换为路径的主要内容,如果未能解决你的问题,请参考以下文章
Java+Maven+Embedded Tomcat:项目拒绝识别网页
Spring Boot 使用Jar打包发布, 并使用 Embedded Jetty/Tomcat 容器
spring 配置 embedded tomcat的acceptCount与maxConnections
Springboot项目编译正常启动Unable to start embedded Tomcat报错
代码适用于嵌入式Apache Tomcat 8但不适用于9。有什么改变?
Spring boot Embedded Tomcat "application/json" post request 限制为 10KB