可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've been trying to find a way to set the context path for a webflux application. I know I can configure it using
server.servlet.context-path
if I deploy a servlet, but I would like to achieve it with webflux, without having to explicitly add the path to every route or use MVC.
回答1:
You can use web filter to make WebFlux support 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:
According to this
There is servlet in the name of the property which should be a
hint that won't work with webflux.
With springboot v2.3, you can put this in your properties file
spring.webflux.base-path=/your-path
release-notes reference: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#configurable-base-path-for-webflux-applications
回答3:
For Undertow I managed to add a context path by creating a customized 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));
}
};
}
回答4:
Here's my way of doing it with 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);
}
}
}
回答5:
If you are configuring yourself the server (if you’re not using Spring Boot), you can setup a ContextPathCompositeHandler that wraps several handlers itself.
If you are using Spring Boot, this feature is not supported currently.
回答6:
I was having the same issue since the loader balancer bases on the context path to route to different back end apps. One way to get around Spring Boot Webflux w/ the context path is using variables in the @XXXXMapping annotations. For instance @RequestMapping(value = "${server.servlet.context-path}/subpath")
回答7:
For use cases where WebFlux application is behind load balancer/proxy you can use dedicated class - ForwardedHeaderTransformer
that will extract path context from X-Forwarded-Prefix
and will add it to ServerHttpRequest
.
Doing so you won't need to modify global context-path
(which does not make sense in WebFlux)
More about it here: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-web-handler-api
回答8:
Here is an example of configuring the context path for WebFlux using Netty server based on a comment by @Dmytro Boichenko. You can also include customizers to configure the port and other properties.
@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);
}
};
}
}
回答9:
You can use web Filter, as mentioned in above answers, but you can do one more thing. Write a Base Controller and Extend every class to that Base Controller.
For example:
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();
}
}
So now you can hit /{base_url}/status