从 Spring Boot 1.5.9 升级到 2.2.6 时,HATEOAS 链接中不考虑上下文路径
Context Path not considered in HATEOAS links when upgrading from Spring Boot 1.5.9 to 2.2.6
我最近将基于 Spring Boot 的旧应用程序从版本 1.5.9 升级到 2.2.6。
不幸的是,升级后,HATEOAS 生成的 url 发生了变化。基本上,现在链接中缺少上下文路径。
示例:
Before: https://domain.test.com/service/api/endpoint
Now: https://domain.test.com/service/endpoint
现在我在应用程序属性中使用以下配置:
server.servlet.context-path: /api
server.forward-headers-strategy: FRAMEWORK
spring.data.rest.basePath: /api
(使用 none,主机完全不同(因为 x-forwarded-host。我也尝试过使用本机,但行为相同)
我还创建了一个 ForwardedHeaderFilter bean。
@Bean
public ForwardedHeaderFilter forwardedHeaderFilter() {
return new ForwardedHeaderFilter();
}
有什么办法可以绕过这个问题吗?我做错了什么吗?
一种替代方法是调整 api 网关,但从业务流程的角度来看,这会非常复杂,因此我更喜欢更技术性的方法。
谢谢!
作为临时解决方案,在我有时间真正深入了解之前,我创建了一个新的实用程序 class,负责调整路径:
public class LinkUtil {
private LinkUtil() {
}
@SneakyThrows
public static <T> Link linkTo(T methodOn) {
String rawPath = WebMvcLinkBuilder.linkTo(methodOn).toUri().getRawPath();
rawPath = StringUtils.remove(rawPath, "/service");
BasicLinkBuilder basicUri = BasicLinkBuilder.linkToCurrentMapping().slash("/api").slash(rawPath);
return new Link(basicUri.toString());
}
}
其中 /api
是上下文路径。
然后我这样使用它:
Link whateverLink = LinkUtil.linkTo(methodOn(WhateverClass.class).whateverMethod(null)).withRel("whatever-rel));
@LoolKovski 的临时解决方案依赖于现有的 ServletRequest,因为 #linkToCurrentMapping。如果您也需要消除该限制,请使用以下代码:
public class LinkUtil {
private LinkUtil() {
}
@SneakyThrows
public static <T> Link linkTo(T methodOn) {
var originalLink = WebMvcLinkBuilder.linkTo(methodOn);
var rawPathWO = StringUtils.remove(originalLink.toUri().getRawPath(), "/service");
return originalLink.withHref("/api" + rawPathWO);
}
}
实际上,在我的例子中,链接是在 RestController bean 之一的初始化过程中生成的,因此我的真实代码类似于以下代码。
我之前不需要切断其他路径部分,只需要在前面加上配置的上下文路径。
@RestController
public class ExampleController implements ServletContextAware {
@Override
public void setServletContext(ServletContext servletContext) {
final var executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
someRepository.getExamples().forEach((name, thing) -> {
Link withRel = linkTo(methodOn(ExampleController.class).getElement(null, name, null))
.withSelfRel();
withRel = withRel.withHref(servletContext.getContextPath() + withRel.toUri().getRawPath());
thing.add(withRel);
});
executor.shutdown();
});
}
@RequestMapping(path = "/{name}/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<Example> getElement(ServletWebRequest req, @PathVariable("name") String name, Principal principal) {
[...]
}
我最近将基于 Spring Boot 的旧应用程序从版本 1.5.9 升级到 2.2.6。
不幸的是,升级后,HATEOAS 生成的 url 发生了变化。基本上,现在链接中缺少上下文路径。
示例:
Before: https://domain.test.com/service/api/endpoint
Now: https://domain.test.com/service/endpoint
现在我在应用程序属性中使用以下配置:
server.servlet.context-path: /api
server.forward-headers-strategy: FRAMEWORK
spring.data.rest.basePath: /api
(使用 none,主机完全不同(因为 x-forwarded-host。我也尝试过使用本机,但行为相同)
我还创建了一个 ForwardedHeaderFilter bean。
@Bean
public ForwardedHeaderFilter forwardedHeaderFilter() {
return new ForwardedHeaderFilter();
}
有什么办法可以绕过这个问题吗?我做错了什么吗?
一种替代方法是调整 api 网关,但从业务流程的角度来看,这会非常复杂,因此我更喜欢更技术性的方法。
谢谢!
作为临时解决方案,在我有时间真正深入了解之前,我创建了一个新的实用程序 class,负责调整路径:
public class LinkUtil {
private LinkUtil() {
}
@SneakyThrows
public static <T> Link linkTo(T methodOn) {
String rawPath = WebMvcLinkBuilder.linkTo(methodOn).toUri().getRawPath();
rawPath = StringUtils.remove(rawPath, "/service");
BasicLinkBuilder basicUri = BasicLinkBuilder.linkToCurrentMapping().slash("/api").slash(rawPath);
return new Link(basicUri.toString());
}
}
其中 /api
是上下文路径。
然后我这样使用它:
Link whateverLink = LinkUtil.linkTo(methodOn(WhateverClass.class).whateverMethod(null)).withRel("whatever-rel));
@LoolKovski 的临时解决方案依赖于现有的 ServletRequest,因为 #linkToCurrentMapping。如果您也需要消除该限制,请使用以下代码:
public class LinkUtil {
private LinkUtil() {
}
@SneakyThrows
public static <T> Link linkTo(T methodOn) {
var originalLink = WebMvcLinkBuilder.linkTo(methodOn);
var rawPathWO = StringUtils.remove(originalLink.toUri().getRawPath(), "/service");
return originalLink.withHref("/api" + rawPathWO);
}
}
实际上,在我的例子中,链接是在 RestController bean 之一的初始化过程中生成的,因此我的真实代码类似于以下代码。 我之前不需要切断其他路径部分,只需要在前面加上配置的上下文路径。
@RestController
public class ExampleController implements ServletContextAware {
@Override
public void setServletContext(ServletContext servletContext) {
final var executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
someRepository.getExamples().forEach((name, thing) -> {
Link withRel = linkTo(methodOn(ExampleController.class).getElement(null, name, null))
.withSelfRel();
withRel = withRel.withHref(servletContext.getContextPath() + withRel.toUri().getRawPath());
thing.add(withRel);
});
executor.shutdown();
});
}
@RequestMapping(path = "/{name}/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<Example> getElement(ServletWebRequest req, @PathVariable("name") String name, Principal principal) {
[...]
}