tomcat的contextpath问题
侧边栏壁纸
博主昵称
yuc

  • 累计撰写 291 篇文章
  • 累计收到 0 条评论

tomcat的contextpath问题

yuc
yuc
2023-08-24 / 最后修改: 2023-08-31 03:03 / 0 评论 / 7 阅读 / 正在检测是否收录...
0x1 问题

一直苦于产品对于上下文路径支持的不够灵活,比如 war 包名为 test.war,那么产品会在内部的请求中加载的资源全部写成 /test 开头,这样即使我们在 nginx 中用重写、反代等手段在前面增加一个上下文,或者完全修改这个上下文也是不行的。只是第一个修改后的请求能到后端,一但服务器返回资源后,请求内仍然是 /test 开头,下面的请求全部不能成功了

0x2 难度

要解决灵活设置上下文的问题,有哪些办法呢?

  1. 请求简单,如果我们只需要一个接口,那么重写没有问题,因为它返回的内容不再有其他资源需要加载或者跳转
  2. 使用相对路径,使用相对路径不在显示上下文,那么可以很好的自己控制上下文是什么
  3. 产品本身实现设置上下文的功能,能够实现路径映射

在上面写的方式中,除了接口我们能够解决外,对于整个产品的上下文仍然没有很好的解决办法。

0x3 折中

在经过了一系列搜索后,终于找到了一个折中的办法,通过tomcat的配置来实现路径映射,如下:

<Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="false" deployOnStartup="false">
<Context path="aaa" docBase="testa" debug="0" reloadable="true"/>
<Context path="bbb" docBase="testb" debug="0" reloadable="true"/>

在Host段内配置Context标签,path为浏览器访问的上下文路径,docBase为webapps下实际物理目录,这两行配置就可以实现通过aaa、bbb访问到之前的testa、testb项目,此时实现了testa、aaa共存的方式访问。

经过测试后,这个方案基本上能够满足修改上下文的需求,但是这个方案也有弊端,它会把项目部署两次,如果是通过war包部署,可以看到解压到了新的目录中。所以其实跟一个war复制到两个名字部署区别不大。

这个方法对于一般项目来说应该没问题,但是有些产品可能启动多个会自动组成集群,那么就可能会有集群问题

0x4 其他方案

上面的方案可能最终导致集群问题,那么还有方案吗?在经过检索后是有的,不过也是一个没有那么好的方案,可以增加上下文路径的级数来变相修改,比如默认是 /test,这种方法是修改为 /aaa/test。一开始以为 tomcat/webapps 下可以通过创建两级目录来实现这个效果,但测试失败,war根本不会部署。需要修改 war 名为 aaa#test.war ,这样解压后目录仍然是 aaa#test,但 # 的间隔符能够让我们在浏览器上使用两级目录访问了。

这种方法虽然仍然不是最优的,但目前测试下来并没有其他问题

0x5 20230830更新

上面的方法中使用 # 来作为 war 包的分隔符使得上下文路径有多级确实是tomcat支持的,所以是一个较好的解决方法,不过这一次经过检索后发现了多种配置方案达到此目的

方案一

修改 server.xml 为如下配置

<Host name="localhost"  appBase="webapps"
     unpackWARs="true" autoDeploy="false" deployOnStartup="true" >
<Context path="/ss/aa" docBase="/opt/tomcat8.5.91/war/test.war" />

比较关键的是 path="/ss/aa"docBase="/opt/tomcat8.5.91/war/test.war" ,第一个参数表示我们想要的上下文路径(contextpath),第二表示war包的路径,这样把war放到外面可以防止二次部署,这是必须的。观察到这样启动后会自动解压缩指定路径的war到 webapps 下,并且目录名是 path 设置的路径。

方案二

修改 server.xml 为如下配置

<Host name="localhost"  appBase="webapps"
    unpackWARs="false" autoDeploy="false" deployOnStartup="false" >
<Context path="/ss/aa" docBase="test.war" />

这个配置跟方案一差别不是很大,最大的区别就是没有指定war的绝对路径,那么会从webapps下识别,为了防止二次部署,需要把上面的 unpackWARS, autoDeploy, deployOnStartup 全部设置为 false,这样的话 webapps 目录下不会产生项目的目录,可防止二次部署的情况。但这种不解压war的方式对项目有一定的要求,有些项目启动的时候会要求从war解压的目录下读取文件,没有解压会报错,如下:

2023-08-30 15:40:22.587 [localhost-startStop-1] ERROR c.s.sys.service.JavaScriptCompiler - ServletContext resource [/dist/sys/nodejs/es6toes5.js] cannot be resolved to absolute file path - web application archive not exp
anded?
java.io.FileNotFoundException: ServletContext resource [/dist/sys/nodejs/es6toes5.js] cannot be resolved to absolute file path - web application archive not expanded?
    at org.springframework.web.util.WebUtils.getRealPath(WebUtils.java:344)
    at org.springframework.web.context.support.ServletContextResource.getFile(ServletContextResource.java:199)
    at com.succez.sys.service.JavaScriptCompiler.setServletContext(JavaScriptCompiler.java:170)
    at org.springframework.web.context.support.ServletContextAwareProcessor.postProcessBeforeInitialization(ServletContextAwareProcessor.java:108)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
方案三

跟上面的方法类似, Context 配置部分是可以在 conf/Catalina/localhost/ 目录下新建xml配置文件实现的。但是这种方案有一些限制,此 xml 文件内的 Context 内不能使用 path 参数,使用不会生效,仅能定义 docBase 指定 war 路径,而 contextpath 路径的指定由此文件名决定,所以 xml 文件需要大概格式如: test#aaa.xml ,那在这种情况下如果 war 也设置成两级目录,那么配置文件是优先生效的,比如:

  1. xml配置文件内 docbase 路径为 /app/war/test#aaa.war
  2. 配置文件名为 aaa#bbb.xml
方案四

开启 tomcat 的管理页面,在管理页面中可以上传 war 包部署,并且设置 path

PS.
  • 不管在上面哪个方案,会解压war包的方案最终都会生成 aaa#bbb 这种形式的目录,说明 tomcat 应该是使用这一种方法实现的定义 contextpath,只是配置方法有不同而已。
  • 所以推荐使用直接命名 war 的方式,或者方案一
参考文档:
https://octopus.com/blog/defining-tomcat-context-paths
0

评论

博主关闭了当前页面的评论