英文:
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发生了变化。基本上,现在的链接中缺少了上下文路径(context-path)。
例如:
之前:https://domain.test.com/service/api/endpoint
现在: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)。我还尝试过使用native,但行为相同)
我还创建了一个ForwardedHeaderFilter bean。
@Bean
public ForwardedHeaderFilter forwardedHeaderFilter() {
return new ForwardedHeaderFilter();
}
我能做些什么来绕过这个问题吗?我做错了什么吗?
一个替代方案是调整API网关,但从业务流程角度来看,这将会非常复杂,所以我更倾向于采用更加技术化的方法。
谢谢!
英文:
I have recently upgraded an older application, based on Spring Boot, from version 1.5.9 to 2.2.6.
Unfortunately, after upgrading, the urls generated with HATEOAS are changed. Basically the context-path is missing from the Links now.
Example:
Before: https://domain.test.com/service/api/endpoint
Now: https://domain.test.com/service/endpoint
Right now I am using the following configs in application properties:
server.servlet.context-path: /api
server.forward-headers-strategy: FRAMEWORK
spring.data.rest.basePath: /api
(With none, the host is totally different(because of the x-forwarded-host. I have also tried with native, but same behavior)
I have also created a ForwardedHeaderFilter bean.
@Bean
public ForwardedHeaderFilter forwardedHeaderFilter() {
return new ForwardedHeaderFilter();
}
Is there anything I can do to bypass this issue? Am I doing something wrong ?
One alternative would be to adjust the api gateway, but this would be really complicated from a business process perspective so I would prefer a more technical approach.
Thank you !
答案1
得分: 0
作为临时解决方案,在我有时间进行深入研究之前,我已经创建了一个新的实用程序类,用于调整路径:
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");
英文:
As a temporary solution, until I have time to really take a deeper look, I have created a new Utility class, that takes care of adjusting the path:
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());
}
}
Where /api
is the context-path.
Then I use it like this:
Link whateverLink = LinkUtil.linkTo(methodOn(WhateverClass.class).whateverMethod(null)).withRel("whatever-rel));
答案2
得分: 0
@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) {
[...]
}
}
英文:
@LoolKovski's temporary solution relies on an existing ServletRequest because of #linkToCurrentMapping. Use the following code if you, too, need to eliminate that restriction:
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);
}
}
Actually, in my case the links are generated during one of the RestController beans' initialization, so my real code looks like the following code.
I don't need to cut-off some other path part before but only need to prepend a configured context path.
@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) {
[...]
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论