Webflux中的上下文路径

29

我一直在尝试找到设置Webflux应用程序上下文路径的方法。我知道可以使用

server.servlet.context-path
如果我部署一个servlet,但是我想用webflux来实现它,而不必在每个路由上明确添加路径或使用MVC。

可能是 更改 Spring Boot 2.0 上下文路径 的重复问题。 - Brian Clozel
1
我不打算使用context-path,我知道它是servlet容器的一部分。我正在寻找一个相当的解决方案来与webflux一起使用。 - Marcus Lindwall
12个回答

54

2
似乎没有任何影响。在此版本中,以及当前的2.4.2版本中都没有影响。为了验证是否存在与基于服务器的方法冲突,我也放置了server.servlet.contextPath并使用了不同的值,但是两者都没有反映出来。只有通过http://localhost:8080/test直接访问被注释为@RequestMapping(path = {"/test"})的控制器时才能接收到。 - Martin Mucha
2
应该在2022年标记为正确答案!!!开箱即用 - Laess3r

16

你可以使用Web过滤器来使WebFlux支持contextPath

@Bean
public WebFilter contextPathWebFilter() {
    String contextPath = serverProperties.getServlet().getContextPath();
    return (exchange, chain) -> {
        ServerHttpRequest request = exchange.getRequest();
        if (request.getURI().getPath().startsWith(contextPath)) {
            return chain.filter(
                exchange.mutate()
                .request(request.mutate().contextPath(contextPath).build())
                .build());
        }
        return chain.filter(exchange);
    };
}

2
此解决方案支持所有服务器,包括Netty。 - 刘家华
1
我尝试过了,但它不起作用。另外,我不理解这段代码。你获取一个具有上下文路径的URL,然后再使用上下文路径更改它?从根本上说,它没有任何变化。 - John Zhang
WebFlux不会自动识别哪部分是上下文路径,因此上述代码使用WebFilter告诉WebFlux每个请求的上下文路径是哪部分。@JohnZhang - 刘家华
嗨,serverProperties是什么? - Melardev
1
@Melardev 类型为 org.springframework.boot.autoconfigure.web.ServerProperties 的 bean - 刘家华
显示剩余4条评论

8

我也曾遇到类似的问题,与spring.webflux.base-path有关(似乎没有按照预期工作),是在webflux-reactive-spring-web中。后来我发现我已将自动配置禁用了。

手动解决方法如下:

@Bean
public WebFluxProperties webFluxProperties(){
    return new WebFluxProperties();
}

spring.webflux.base-path 在 Spring Boot 2.4.7 中对我起了作用。 - Philippe
你好,@Philippe。我正在使用Spring Boot 2.4.7,但是spring.webflux.base-path对我不起作用。有什么想法吗? - James
@James 你确认你正在使用的是Spring Boot 2.4.7吗?当你启动项目时,版本会在日志中打印出来。如果你使用gradle作为构建系统,你可以使用dependencyInsight任务来确认引入的Spring Boot依赖版本。自2.3版本以来,属性键spring.webflux.base-path一直存在,如https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#configurable-base-path-for-webflux-applications中所述。 - Philippe

4
这里是使用Tomcat Reactive实现的方法:

首先,我们需要添加以下依赖项:

@Configuration
public class TomcatReactiveWebServerConfig extends TomcatReactiveWebServerFactory {

    @Value("${server.servlet.context-path}")
    private String contextPath;

    /**
     * {@inheritDoc}
     */
    @Override
    protected void configureContext(final Context context) {

        super.configureContext(context);

        if (StringUtils.isNotBlank(this.contextPath)) {
            context.setPath(this.contextPath);
        }
    }
}

3

对于Undertow,我成功添加了上下文路径,方法是创建一个自定义的UndertowReactiveWebServerFactory:

 @Bean
public UndertowReactiveWebServerFactory undertowReactiveWebServerFactory(
        @Value("${server.servlet.context-path}") String contextPath) {
    return new UndertowReactiveWebServerFactory() {
        @Override
        public WebServer getWebServer(HttpHandler httpHandler) {
            Map<String, HttpHandler> handlerMap = new HashMap<>();
            handlerMap.put(contextPath, httpHandler);
            return super.getWebServer(new ContextPathCompositeHandler(handlerMap));
        }
    };
}

2
该解决方案可以轻松适用于NettyReactiveWebServerFactory - Dmytro Boichenko
非常感谢,目前这是一个完美而优雅的解决方案。理想情况下,可以通过实例化适当的bean并使用条件注释(@Conditional annotations)来确定应用程序中使用哪个服务器,从而添加所有三个可用服务器(netty、tomcat和undertow)的工厂。这样,即使您切换容器,也会创建正确的bean并添加上下文路径。 - skryvets

2

这是一个很棒的答案,我希望它能得到更多的投票。 - Emmanuel John

1

Spring webflux版本为2.3.4.RELEASE

需要两个元素,首先声明一个bean以启用weflux属性

@Bean public WebFluxProperties webFluxProperties(){
return new WebFluxProperties(); 
}

第二步是定义正确的路径

spring.webflux.base-path = mypath

1
你可以尝试这个解决方案,希望能帮到你。

https://github.com/spring-projects/spring-boot/issues/22162

在我的情况下,我按照以下建议逐步操作:

  1. 将spring-boot-starter-web和spring-boot-starter-webflux添加到pom.xml中
  2. 添加spring.webflux.base-path=/service-name
  3. 更新spring boot应用程序的主类

SpringApplication springApplication = new SpringApplication(DscUserManagementService.class); springApplication.setWebApplicationType(WebApplicationType.REACTIVE); springApplication.run(args);

  1. 最后,尝试API ip:port/service-name/...

谢谢wilkinsona


1
您可以像上面的答案中提到的那样使用Web Filter,但是您还可以做一件事情。编写一个基础控制器并将每个类扩展到该基础控制器。 例如:

Base Controller.java

@RestController
@RequestMapping("/{base_url}")
public abstract class BaseController {
}

NewController.java

@RestController
public class NewController extends BaseController{
  @Autowired
  DatabaseClient databaseClient;

  @GetMapping("/status")
  public Mono<Map<String, String>> status() {
    return databaseClient.execute("SELECT 'ok'").
      map(row -> singletonMap("status", row.get(0, String.class)))
      .one();
  }
}

现在你可以访问/{base_url}/status。

非常感谢。这帮助我为特定一组控制器获取了“基本路径”,同时保持/actuator/health在根级别。 - Kai Stapel

1
这是一个使用基于Netty服务器的WebFlux配置上下文路径的示例,基于@Dmytro Boichenko的评论。您还可以包括自定义程序以配置端口和其他属性。
@Configuration
public class NettyServerConfig {

    @Value("${server.port}")
    private int port;

    @Value("${server.context.path}")
    private String contextPath;

    @Bean
    public NettyReactiveWebServerFactory nettyReactiveWebServerFactory() {
            NettyReactiveWebServerFactory webServerFactory = new NettyReactiveWebServerFactory() {
                @Override
                public WebServer getWebServer(HttpHandler httpHandler) {
                    Map<String, HttpHandler> handlerMap = new HashMap<>();
                    handlerMap.put(contextPath, httpHandler);
                    return super.getWebServer(new ContextPathCompositeHandler(handlerMap));
                }
        };
        webServerFactory.addServerCustomizers(portCustomizer());
        return webServerFactory;
    }

    public NettyServerCustomizer portCustomizer() {
        return new NettyServerCustomizer() {
            @Override
            public HttpServer apply(HttpServer httpServer) {
                return httpServer.port(port);
            }
        };
    }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接