“从Spring Boot 1.5.9升级到2.2.6时,HATEOAS链接中未考虑上下文路径。”

huangapple go评论68阅读模式
英文:

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 &lt;T&gt; Link linkTo(T methodOn) {
        String rawPath = WebMvcLinkBuilder.linkTo(methodOn).toUri().getRawPath();
        rawPath = StringUtils.remove(rawPath, &quot;/service&quot;);
        BasicLinkBuilder basicUri = BasicLinkBuilder.linkToCurrentMapping().slash(&quot;/api&quot;).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(&quot;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 &lt;T&gt; Link linkTo(T methodOn) {
var originalLink = WebMvcLinkBuilder.linkTo(methodOn);
var rawPathWO = StringUtils.remove(originalLink.toUri().getRawPath(), &quot;/service&quot;);
return originalLink.withHref(&quot;/api&quot; + 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(() -&gt; {
someRepository.getExamples().forEach((name, thing) -&gt; {
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 = &quot;/{name}/&quot;, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity&lt;Example&gt; getElement(ServletWebRequest req, @PathVariable(&quot;name&quot;) String name, Principal principal) {
[...]
}

huangapple
  • 本文由 发表于 2020年4月9日 22:57:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/61124099.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定