带有子路径的Spring应用上下文设置

带有子路径的Spring应用上下文设置

在开发中,程序员往往启动应用能跑就好了,但到了线上环境,应用不一定能够分配到一个域名,从而需要在 URL 上设置前缀

1
www.example.com/spring-app

对于该情况网络上通常使用 Nginx 进行反向代理,一个示范配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# nginx
server {
listen 80;
listen 443;
server_name www.example.com;

location /spring-app/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;

proxy_pass http://ip:port/;
}
}

问题

该配置基本能够满足常见应用的需求,但却仍有缺憾,虽然大多数 api 已经被成功代理,但少数依赖于 ContextPath 对象的方法将发生意料外的行为

因为此时应用的server.servlet.context-path按默认配置为/,按对外部来讲却是/spring-app/,这将导致以下几种常见现象

  1. api 间有重定向行为,但重定向到了以/为基准的路径上,但因为 nginx 反向代理,导致地址错误
  2. 静态资源加载依赖于 ContextPath,但此时 ContextPath 为/导致资源未正常加载

解决方法

设置server.servlet.context-path

针对于 ContextPath 的问题,最简单的便是直接变更应用的 server.servlet.context-path, 配置如下

1
2
3
4
# application.yaml
server:
servlet:
context-path: /spring-app/

这样以上两个问题就能够被解决,但该方案也有自己的问题:

  1. 一个应用只能指定一个 ContextPath,若存在多个不同子路径的反向代理,该方法就无法正常运转
  2. 应用已经在内网被其他服务引用了,但又需要代理到外网的子路径给其他外部服务使用,且内网应用无法修改引用的地址(比如维护人员已经全部离场)

设置server.forward-headers-strategy

1
2
3
# application.yaml
server:
forward-headers-strategy: framework
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# nginx
server {
listen 80;
listen 443;
server_name www.example.com;

location /spring-app/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /spring-app/;
proxy_set_header Host $http_host;

proxy_pass http://ip:port/;
}
}

通过设置在 spring boot 的配置文件中将server.forward-headers-strategy设置为framework,并且在 nginx 设置X-Forwarded-Prefix为与 location 一致,每个请求执行时应用的 ContextPath 将与X-Forwarded-Prefix一致