java - Spring Boot 5 WebClient 在检查 HTTP 响应 header 之前先验证 HTTPStatus

标签 java spring spring-boot spring-webflux

我正在尝试使用 Spring 5 WebClient 确认 HTTP 响应 header 的值,但前提是 Web 调用以 HTTP 200 状态代码进行响应。在此用例中,如果身份验证不成功,API 调用将返回 HTTP 401,且不存在响应 header 。我下面的代码可以正常工作,但它进行了两次网络调用(因为我阻止了两次)。除了仅阻止 HTTP 响应 header ,并在 header 不存在时对 NPE 进行 try/catch 之外,是否有任何“更干净”的方法可以做到这一点?

import java.net.URI;
import java.time.Duration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import org.springframework.web.reactive.function.client.ExchangeFunctions;


import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@SpringBootApplication
public class ContentCheckerApplication {

private static final Logger LOGGER = LoggerFactory.getLogger(ContentCheckerApplication.class);

private ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());         

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(ContentCheckerApplication.class);
    // prevent SpringBoot from starting a web server
    app.setWebApplicationType(WebApplicationType.NONE);
    app.run(args);
}

@Bean
public CommandLineRunner myCommandLineRunner() {

    return args -> {
              // Our reactive code will be declared here
        LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();

        formData.add("username", args[2]);
        formData.add("password", args[3]);

        ClientRequest request = ClientRequest.method(HttpMethod.POST, new URI(args[0]+"/api/token"))
                .body(BodyInserters.fromFormData(formData)).build();

        Mono<ClientResponse> mresponse = exchange.exchange(request);
        Mono<String> mnewToken = mresponse.map(response -> response.headers().asHttpHeaders().getFirst("WSToken"));
        LOGGER.info("Blocking for status code...");
        HttpStatus statusCode = mresponse.block(Duration.ofMillis(1500)).statusCode();
        LOGGER.info("Got status code!");

        if (statusCode.value() == 200) {

            String newToken = mnewToken.block(Duration.ofMillis(1500));
            LOGGER.info("Auth token is: " + newToken);

        } else {
            LOGGER.info("Unable to authenticate successfully! Status code: "+statusCode.value());
        }
       };
    }
}

最佳答案

感谢@M 的评论。 Deinum 来指导我,我有以下代码,现在可以使用。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import org.springframework.web.reactive.function.client.ExchangeFunctions;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

@SpringBootApplication
public class ContentCheckerApplication {

private static final Logger LOGGER = LoggerFactory.getLogger(ContentCheckerApplication.class);

private ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());         

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(ContentCheckerApplication.class);
    // prevent SpringBoot from starting a web server
    app.setWebApplicationType(WebApplicationType.NONE);
    app.run(args);
}

@Bean
public CommandLineRunner myCommandLineRunner() {

    return args -> {
              // Change some Netty defaults
        ReactorClientHttpConnector connector = new ReactorClientHttpConnector(
                  options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
                                    .compression(true)
                                    .afterNettyContextInit(ctx -> {
                                        ctx.addHandlerLast(new ReadTimeoutHandler(1500, TimeUnit.MILLISECONDS));
                                    }));


        LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();

        formData.add("username", args[2]);
        formData.add("password", args[3]);

        WebClient webClient = WebClient.builder().clientConnector(connector).build();

            Mono<String> tokenResult = webClient.post()
                    .uri( args[0] + "/api/token" )
                    .body( BodyInserters.fromFormData(formData))
                    .exchange()
                    .onErrorMap(ContentCheckerApplication::handleAuthTokenError)
                    .map(response -> {

                            if (HttpStatus.OK.equals(response.statusCode())) {
                                return response.headers().asHttpHeaders().getFirst("WSToken");
                            } else {
                                return "";
                            }

                    });

            LOGGER.info("Subscribing for the result and then going to sleep");
            tokenResult.subscribe(ContentCheckerApplication::handleAuthTokenResponse);

        Thread.sleep(3600000);
       };
    }

private static Throwable handleAuthTokenError(Throwable e) {
    LOGGER.error("Exception caught trying to process authentication token. ",e);
    ContentCheckerApplication.handleAuthTokenResponse("");      
    return null;        
}

private static void handleAuthTokenResponse(String newToken) {

    LOGGER.info("Got status code!");

    if (!newToken.isEmpty()) {

        LOGGER.info("Auth token is: " + newToken);

    } else {
        LOGGER.info("Unable to authenticate successfully!");
    }

    System.exit(0);
}
}

关于java - Spring Boot 5 WebClient 在检查 HTTP 响应 header 之前先验证 HTTPStatus,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47512766/

相关文章:

java - 使用 Junit 进行 Jersey Restful 测试

java - 将数据库中BLOB字段中的二进制内容转换为文件mySQL

java - 在循环中杀死一个非终止进程

java - 字段列表中的 Spring Boot JPA 未知列

java - 是否可以为 @Valid 参数异常创建多个自定义验证消息?

spring-boot - Kotlin 1.3 + Spring Boot : There is already '...' bean method

java - PHP-CGI 帖子空

java - 如何使用Logback以JSON方式登录?

java - Spring Boot中接受来自特定ip的请求

spring - 您如何应对 Spring beans 增加的额外复杂性?