【问题标题】:Context Path with WebfluxWebflux 的上下文路径
【发布时间】:2018-08-18 04:18:05
【问题描述】:

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

进行配置
server.servlet.context-path

如果我部署了一个 servlet,但我想用 webflux 来实现它,而不必显式地将路径添加到每个路由或使用 MVC。

【问题讨论】:

  • 我不打算使用上下文路径,我意识到它是 servlet 容器的一部分。我正在寻找与 webflux 一起使用的等效解决方案。

标签: spring-webflux


【解决方案1】:

根据this

属性名中有servlet,应该是 提示不适用于 webflux。

使用 springboot v2.3,你可以把它放在你的属性文件中

spring.webflux.base-path=/your-path

发行说明参考:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#configurable-base-path-for-webflux-applications

【讨论】:

  • 似乎没有任何效果。不在此版本中,不在当前 2.4.2 中。为了验证与基于servet的方法没有冲突,我还放了具有不同值的server.servlet.contextPath,但两者都没有反映,并且@RequestMapping(path = {"/test"})注释的控制器只能访问直接通过localhost:8080/test.
  • 2022年应该标记为正确答案!!!开箱即用
【解决方案2】:

您可以使用网络过滤器使 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);
    };
}

【讨论】:

  • 该方案支持所有服务器,包括Netty
  • 我试过了,但它不起作用。另外,我不明白代码。你得到一个带有上下文路径的 url,然后用上下文路径更改它?基本上没有变化。
  • WebFlux 不会自动识别哪个部分是上下文路径,所以上面的代码使用 WebFilter 来告诉 WebFlux 哪个部分是每个请求的上下文路径。 @JohnZhang
  • 我在我的类中复制了上面的代码,并带有一些注释,如@Configuration 等,然后我将第一条语句更改为'String contextPath = "api"'。但是,我无法访问localhost/api.... 在调试模式下,代码没有被调用。有什么想法吗?
  • @Melardev bean 类型为 org.springframework.boot.autoconfigure.web.ServerProperties
【解决方案3】:

我在webflux-reactive-spring-web 中遇到了与spring.webflux.base-path 类似的问题(似乎没有按预期工作),我意识到我禁用了自动配置。

手动解决方法是:

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

【讨论】:

  • spring.webflux.base-path 在 Spring Boot 2.4.7 中为我工作
【解决方案4】:

对于 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));
        }
    };
}

【讨论】:

  • 该解决方案可以轻松适应NettyReactiveWebServerFactory
  • 谢谢您,目前完美且最优雅的解决方案。理想情况下,可以通过实例化适当的 bean 来添加所有三个可用服务器(netty、tomcat 和 undertow)的工厂,然后使用条件(@Conditional 注释)来确定在应用程序中使用哪个服务器。这样,即使切换容器,也会创建正确的 bean 并添加上下文路径
【解决方案5】:

这是我使用 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);
        }
    }
}

【讨论】:

    【解决方案6】:

    对于 WebFlux 应用程序位于负载均衡器/代理之后的用例,您可以使用专用类 - ForwardedHeaderTransformer,它将从 X-Forwarded-Prefix 提取路径上下文并将其添加到 ServerHttpRequest

    这样做你不需要修改全局context-path(这在 WebFlux 中没有意义)

    更多信息在这里:https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-web-handler-api

    【讨论】:

      【解决方案7】:

      这是一个使用 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);
                  }
              };
          }
      }
      
      

      【讨论】:

        【解决方案8】:

        您可以使用网络过滤器,如上述答案中所述,但您还可以做一件事。编写一个基本控制器并将每个类扩展到该基本控制器。 例如:

        基本 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 保持在根级别。
        【解决方案9】:

        如果您正在配置自己的服务器(如果您不使用 Spring Boot),您可以设置 a ContextPathCompositeHandler 自己包装多个处理程序。

        如果您使用的是 Spring Boot,目前不支持此功能。

        【讨论】:

        • 谢谢,我们主要使用 Spring Boot,所以很遗憾我们现在不能使用它。希望将来会添加对此的支持,它将简化我们在现有管道中向 Spring Boot 2 的过渡。
        • 您打算如何使用它?我描述的 Spring Framework 特性意味着您可以在同一个应用程序中部署多个处理程序,即不在同一个容器上部署多个应用程序。
        • 我们的负载均衡配置要求每个应用程序都有一个上下文路径,但我们已经在管道中解决了这个限制。
        • 也使用 Spring Boot + WebFlux,我的解决方法是写一个 WebFilter@Order(HIGHEST_PRECEDENCE) 并从任何传入请求中删除上下文路径,以便应用程序的其余部分不知道上下文。
        【解决方案10】:

        我遇到了同样的问题,因为加载器平衡器基于上下文路径路由到不同的后端应用程序。使用上下文路径绕过 Spring Boot Webflux 的一种方法是在 @XXXXMapping 注释中使用变量。例如@RequestMapping(value = "${server.servlet.context-path}/subpath")

        【讨论】:

        • 这是我的第一个方法,但对于大型应用程序和微服务架构来说,它的可持续性不是很高。
        【解决方案11】:

        Spring webflux 版本 2.3.4.RELEASE

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

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

        第二次定义正确的路径

        spring.webflux.base-path = mypath
        

        【讨论】:

          猜你喜欢
          • 2021-07-04
          • 2019-01-11
          • 1970-01-01
          • 2018-09-09
          • 2011-01-04
          • 2011-01-02
          • 1970-01-01
          • 1970-01-01
          • 2019-08-22
          相关资源
          最近更新 更多