Spring-cloud-gateway: How to know the resonpse body (i.e. string format) is null or empty in spring-cloud-gateway?

Created on 20 Nov 2019  路  8Comments  路  Source: spring-cloud/spring-cloud-gateway

ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(exchange.getResponse()) {
    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        if (body instanceof Flux) {
            Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
            // can i know response body string is null/empty by fluxBody, how? 
        }
       return super.writeWith(body);
    }
};

Thx.

question

All 8 comments

You could check the Content-Length or Transfer-Encoding headers using the getHeaders() method. That method will retrieve the headers from the original response.

I tried just now, the Content-Length is -1 when downstream returns a json, and it is 0 when get null from downstream.
I checked the javadoc in org.springframework.http.HttpHeaders#getContentLength, it said:

    /**
     * Return the length of the body in bytes, as specified by the
     * {@code Content-Length} header.
     * <p>Returns -1 when the content-length is unknown.
     */

Why Content-Length is -1 when downstream return a json? Is there something wrong with downstream?
My downstream is just a springboot application with springcloud-config and eureka configured.
SpringBoot Version: 2.1.6.RELEASE
SpringCloud Version: Greenwich.RELEASE

I checked many times and got the following results from a SpingBoot application.

  • Content-Length is missing and Transfer-Encoding is "chunked"
  • Content-Length is 0 and Transfer-Encoding is missing

The following code snippets works for me, but I am not sure whether it is exact enough .

ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(exchange.getResponse()) {
    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        if (body instanceof Flux) {
            Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;

            String transferEncoding = this.getDelegate().getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING);
            long contentLength = this.getDelegate().getHeaders().getContentLength();

            // when "Transfer-Encoding" is null && "Content-Length" is 0
            if (null == transferEncoding && 0 == contentLength) {
                // do something
            }
        }
       return super.writeWith(body);
    }
};

@billysAnna in case you don't trust those headers - you can read the body and get it's lenght

@kkocel I tried, but the following code snippets not work.

Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
Flux<DataBuffer> respDataBuffer = fluxBody.buffer().map(dataBuffers -> {
    // when get null from downstream, cannot go here
});

So, I want to know whether fuluxBody can indicate that or not.

switchOnEmpty()?

Please take a look at the snippet below to work with those data buffers.
Try to always use the DataBufferUtils class since those util methods also take retaining/releasing byte buffers into account.
I didn't test this prior to posting it since I don't know your use case but this might point you in the right direction.

Hope this helps!

ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(exchange.getResponse()) {
    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        Mono<DataBuffer> newBody = DataBufferUtils.join(body)
            .map(content -> {
                // Using readPosition() and readableByteCount() you can determine if the body is empty.
                // You can also take the HTTP headers into account again.
                // In case null and empty should behave the same you could return Mono.empty() here.
            })
            .switchIfEmpty(/* Provide a fallback in case there was no content. */)
        return super.writeWith(newBody);
    }
};

@TYsewyn @spencergibb I tried what you said. That works for me.

Still another way is to let the downstream return a wrapped object instead of a simple one. if the downstream return a User object, tell them to return Rest wrapper object.

public class Rest<T> {
     private int errorCode;
     private String errorMsg;
     private T data;
     // getter and setter ....
}

Thus, an api named /user/findById/{id} will return Rest object even the user with specified id not exists.,
which will always response a non-null object back to gateway. And I do not face the question any more. :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aresa7796 picture aresa7796  路  6Comments

xfworld picture xfworld  路  3Comments

pravinkumarb84 picture pravinkumarb84  路  7Comments

chsi13 picture chsi13  路  6Comments

manishonline picture manishonline  路  3Comments