在webflux中使用WebFilter corsFilter时,为什么请求返回404状态?

时间:2019-07-14 21:00:55

标签: java spring-boot cors spring-webflux

我想使用功能端点在Spring Webflux中构建REST api。对于CORS,我使用WebFilter corsFilter方法来设置所需的标头。我确实看到了该方法的调用(我从中看到了日志消息),并且看到响应上的标头确实是我在Webflux api中设置的标头。但是,一旦我开始使用corsFilter,请求就会返回404状态(之前它们将返回JSON)。我怀疑corsFilter不会将请求传递给路由器功能。为什么会这样?

具体来说,我想知道这条线是否足以将cors配置与路由连接起来:

HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(RouterFunctions.toWebHandler(route))
        .applicationContext(ctx).build();

这是我的主要课程:

package com.mypackage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import reactor.ipc.netty.http.server.HttpServer;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
import static org.springframework.web.reactive.function.server.RequestPredicates.method;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;

@SpringBootApplication
public class Server {
    private static final Logger log = LogManager.getLogger(Server.class);
    public static final String HOST = "localhost";
    public static final int PORT = 8080;

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(CorsConfiguration.class);
        Server server = new Server();
        server.startReactorServer(ctx);

        System.out.println("Press ENTER to exit.");
        System.in.read();
    }

    public RouterFunction<ServerResponse> routingFunction() {
        PersonRepository repository = new DummyPersonRepository();
        PersonHandler handler = new PersonHandler(repository);

        return nest(path("/person"),
                nest(accept(APPLICATION_JSON),
                        route(GET("/{id}"), handler::getPerson)
                                .andRoute(method(HttpMethod.GET), handler::listPeople)
                ).andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::createPerson));
    }

    public void startReactorServer(AnnotationConfigApplicationContext ctx) {
        RouterFunction<ServerResponse> route = this.routingFunction().filter((request, next) -> {
            log.warn(request.path());

            if (request.path().contains("person")) {
                log.warn("calling next()");
                return next.handle(request);
            } else {
                return ServerResponse.status(UNAUTHORIZED).build();
            }
        });

        HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(RouterFunctions.toWebHandler(route))
        .applicationContext(ctx).build();

        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
        HttpServer server = HttpServer.create(HOST, PORT);
        server.newHandler(adapter).block();
    }
}

这是我的CORS配置类:

package com.mypackage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

@Configuration
@EnableWebFlux
public class CorsConfiguration {

    private static final Logger log = LogManager.getLogger(CorsConfiguration.class);

    private static final String ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN, mode";
    private static final String ALLOWED_METHODS = "GET, PUT, POST, DELETE, OPTIONS";
    private static final String ALLOWED_ORIGIN = "*";
    private static final String MAX_AGE = "3600";

    @Bean
    public WebFilter corsFilter() {
        log.warn("from CorsConfiguration!!!");
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
            ServerHttpRequest request = ctx.getRequest();
            log.warn("after ServerHttpRequest");
            if (CorsUtils.isCorsRequest(request)) {
                log.warn("inside isCorsRequest");
                ServerHttpResponse response = ctx.getResponse();
                HttpHeaders headers = response.getHeaders();
                headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
                headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
                headers.add("Access-Control-Max-Age", MAX_AGE);
                headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
                if (request.getMethod() == HttpMethod.OPTIONS) {
                    response.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                }
            }
            return chain.filter(ctx);
        };
    }
}

1 个答案:

答案 0 :(得分:1)

要在定义端点时使用功能性方法,Spring Boot的官方文档中有一个非常简单的示例。

FooBarApplication.class,这是我们的主类。

@SpringBootApplication
public class FooBarApplication {

    public static void main(String[] args) {
        SpringApplication.run(FooBarApplication.class, args);
    }

}

RoutingConfiguration.class(或任何您想调用的名称)

@Configuration
public class RoutingConfiguration {

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
        return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
                .andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
                .andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
    }

}

@Component
public class UserHandler {

    public Mono<ServerResponse> getUser(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        // ...
    }
}

任何带有@Configuration注释的类都将在启动时运行,并运行所有带有@Bean注释的方法。因此,这将运行monoRouterFunction并为我们设置所有路线。

示例取自官方的Spring Boot文档Spring boot webflux向下滚动一点。

编辑: 另外,@EnableWebFlux注释表示您将禁用webflux的自动配置并手动设置upp配置。如果您刚刚起步,我不建议这样做(我知道这个名称很容易让人误解),您可以在Spring WebFlux Auto-configuration

上阅读有关webflux自动配置的信息。

EDIT2: WebFlux具有内置的CorsFilter,您只需配置即可使用。

@Bean
CorsWebFilter corsWebFilter() {
    CorsConfiguration corsConfig = new CorsConfiguration();
    corsConfig.setAllowedOrigins(Arrays.asList("http://allowed-origin.com"));
    corsConfig.setMaxAge(8000L);
    corsConfig.addAllowedMethod("PUT");
    corsConfig.addAllowedHeader("Baeldung-Allowed");

    UrlBasedCorsConfigurationSource source =
  new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", corsConfig);

    return new CorsWebFilter(source);
}

示例取自Enabling CORS with a WebFilter