如何在云 Api 网关的响应正文中添加一些数据

How to add some data in body of response for Cloud Api Gateway

我正在向云 api 网关添加一些身份验证逻辑。我添加了 GatewayFilter:

import java.util.List;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

public class AuthorizationFilter implements GatewayFilter {
  @Override
  public Mono<Void> filter(
    ServerWebExchange exchange, GatewayFilterChain chain) {
    List<String> authorization = exchange.getRequest().getHeaders().get("Authorization");
    if (CollectionUtils.isEmpty(authorization) &&
      !PatternMatchUtils.simpleMatch(URL_WITHOUT_AUTH, exchange.getRequest().getURI().toString())) {
      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      //Add some custom data in body of the response
      return exchange.getResponse().setComplete();
    }
    String token = authorization.get(0).split(" ")[1];
    // token validation
    return chain.filter(exchange);
  }
}

但我无法将一些数据添加到响应正文中。你能帮我看看它是如何工作的以及我如何定制它吗?

P.S。 我正在尝试使用 flux 添加一些数据作为响应,但它不起作用:

 DataBuffer b = exchange.getResponse().bufferFactory().allocateBuffer(256);
      b.write("12345".getBytes());
      return exchange.getResponse().writeWith(s -> Flux.just(b));

我做错了什么?

您应该使用 ServerHttpResponseDecorator 来修改响应。

你的代码应该是这样的:

import java.util.List;

import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import reactor.core.publisher.Mono;

public class AuthorizationFilter implements GatewayFilter {
  @Override
  public Mono<Void> filter(
    ServerWebExchange exchange, GatewayFilterChain chain) {
    List<String> authorization = exchange.getRequest().getHeaders().get("Authorization");
    if (CollectionUtils.isEmpty(authorization) &&
      !PatternMatchUtils.simpleMatch(URL_WITHOUT_AUTH, exchange.getRequest().getURI().toString())) {
      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);

      ServerHttpResponse originalResponse = exchange.getResponse();
      DataBufferFactory bufferFactory = originalResponse.bufferFactory();
      ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
        @Override
        public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
            if (body instanceof Flux) {
                Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                return super.writeWith(fluxBody.map(dataBuffer -> {
                    // probably should reuse buffers 
                    byte[] content = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(content);
                    byte[] uppedContent = new String(content, Charset.forName("UTF-8")).toUpperCase().getBytes();
                    return bufferFactory.wrap(uppedContent);
                }));
            }
            return super.writeWith(body); // if body is not a flux. never got there.
        }           
      };
      return chain.filter(exchange.mutate().response(decoratedResponse).build()); // replace response with decorator
    }
    String token = authorization.get(0).split(" ")[1];
    // token validation
    return chain.filter(exchange);
  }
}

您可以找到 a complete example here.

在 spring 人的帮助下,我成功了。因此,我不得不抛出自定义异常并正确处理它,而不是直接写入响应:

@Bean
public ErrorWebExceptionHandler myExceptionHandler() {
  return new MyWebExceptionHandler();
}

public class MyWebExceptionHandler implements ErrorWebExceptionHandler {
  @Override
  public Mono<Void> handle(
    ServerWebExchange exchange, Throwable ex) {
    byte[] bytes = "Some text".getBytes(StandardCharsets.UTF_8);
    DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
    return exchange.getResponse().writeWith(Flux.just(buffer));
  }
}

这是一个可行的解决方案

import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class AuthorizationFilter implements GatewayFilter {
  @Override
  public Mono<Void> filter(
      ServerWebExchange exchange, GatewayFilterChain chain) {
   if (isAuthorizationTokenValid(exchange.getRequest().getHeaders().get("Authorization"))) 
       return chain.filter(exchange);
    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
    //Add some custom data in body of the response,
    //Returning "Unauthorized" in the body here
    return exchange.getResponse().writeWith(Flux.just(new DefaultDataBufferFactory().wrap("Unauthorized".getBytes())));
  }
  private boolean isAuthorizationTokenValid(List<String> authorizationTokens){
    //Your logic here
    return true;
  }
}