SpringCloudGateway 统一处理响应

在 Gateway 中统一处理响应数据:

/**
 * 响应体转换处理
 *
 * @author heguitang
 */
@Component
public class HttpResponseBodyGlobalFilter implements GlobalFilter, Ordered {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public int getOrder() {
        // -1 is response write filter, must be called before that
        return WRITE_RESPONSE_FILTER_ORDER - 2;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        logger.info("global filter HttpResponseBody,processing response results");

        // 这里可以增加一些业务判断条件,进行跳过处理

        ServerHttpResponse response = exchange.getResponse();
        DataBufferFactory bufferFactory = response.bufferFactory();
        // 响应装饰
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                logger.info("global filter HttpResponseBody,Response processing,getStatusCode={}", getStatusCode());
                if (getStatusCode() != null && body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        // 如果响应过大,会进行截断,出现乱码,看api DefaultDataBufferFactory
                        // 有个join方法可以合并所有的流,乱码的问题解决
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer dataBuffer = dataBufferFactory.join(dataBuffers);
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        // 释放掉内存
                        DataBufferUtils.release(dataBuffer);

                        List<String> encodingList = exchange.getResponse().getHeaders().get(HttpHeaders.CONTENT_ENCODING);
                        boolean zip = encodingList != null && encodingList.contains("gzip");
                        // responseData就是response的值,就可查看修改了
                        String responseData = getResponseData(zip, content);

                        // 重置返回参数
                        String result = responseConversion(responseData);
                        byte[] uppedContent = getUppedContent(zip, result);
                        response.getHeaders().setContentLength(uppedContent.length);
                        response.setStatusCode(HttpStatus.OK);

                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
        // replace response with decorator
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    private String responseConversion(String result) {
        try {
            logger.info("响应结果为:{}", result);
            // 返回值基本数据类型、返回对象、数组的判断
            RestResponse<Object> restResponse = RestResponse.ok(result);
            return JSONUtil.toJsonStr(restResponse);
        } catch (Exception e) {
            logger.error("响应包装转换失败,异常信息为:", e);
            return result;
        }
    }

    private String getResponseData(boolean zip, byte[] content) {
        String responseData;
        if (zip) {
            responseData = GZIPUtils.uncompressToString(content);
        } else {
            responseData = new String(content, StandardCharsets.UTF_8);
        }
        return responseData;
    }


    private byte[] getUppedContent(boolean zip, String result) {
        byte[] uppedContent;
        if (zip) {
            uppedContent = GZIPUtils.compress(result);
        } else {
            uppedContent = result.getBytes(StandardCharsets.UTF_8);
        }
        return uppedContent;
    }
    
}

注意点:

  • 返回的数据,不一定是一次性全返回,因此,需要对数据进行拼接。
  • 返回的数据可能是经过压缩的,所以在获取数据的时候,需要先解压。

工具类 GZIPUtils 如下:

public class GZIPUtils {


    private static final Logger logger = LoggerFactory.getLogger(GZIPUtils.class);

    public static final String GZIP_ENCODE_UTF_8 = "UTF-8";

    public static final String GZIP_ENCODE_ISO_8859_1 = "ISO-8859-1";

    public static final int byteLength = 1024;

    /**
     * 字符串压缩为GZIP字节数组
     */
    public static byte[] compress(String str) {
        return compress(str, GZIP_ENCODE_UTF_8);
    }

    /**
     * 字符串压缩为GZIP字节数组
     */
    public static byte[] compress(String str, String encoding) {
        if (str == null || str.length() == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(str.getBytes(encoding));
            gzip.close();
        } catch (IOException e) {
            logger.error("gzip compress error.", e);
        }
        return out.toByteArray();
    }

    /**
     * GZIP解压缩
     */
    public static byte[] uncompress(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[byteLength];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (IOException e) {
            logger.error("gzip uncompress error.", e);
        }
        return out.toByteArray();
    }

    /**
     * 解压
     */
    public static String uncompressToString(byte[] bytes) {
        return uncompressToString(bytes, GZIP_ENCODE_UTF_8);
    }

    /**
     * 解压
     */
    public static String uncompressToString(byte[] bytes, String encoding) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[byteLength];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
            return out.toString(encoding);
        } catch (IOException e) {
            logger.error("gzip uncompress to string error.", e);
        }
        return null;
    }

    public static void main(String[] args) {
        String str = "%5B%7B%22lastUpdateTime%22%3A%222011-10-28+9%3A39%3A41%22%2C%22smsList%22%3A%5B%7B%22liveState%22%3A%221";
        System.out.println("原长度:" + str.length());
        System.out.println("压缩后字符串:" + GZIPUtils.compress(str).toString().length());
        System.out.println("解压缩后字符串:" + StringUtils.newStringUtf8(GZIPUtils.uncompress(GZIPUtils.compress(str))));
        System.out.println("解压缩后字符串:" + GZIPUtils.uncompressToString(GZIPUtils.compress(str)));
    }

}

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud Gateway 是一个高性能且轻量级的 API 网关,它是 Spring Cloud 家族的一员,用于路由、过滤和服务发现。在 Gateway 中,异常处理是一个关键功能,它可以帮助你在全局层面上捕获和处理请求过程中可能出现的各种错误。 在 Spring Cloud Gateway 中,你可以使用全局错误处理器(Global Error Handler)来统一处理所有未被路由到其他服务的异常。这样,即使内部微服务发生异常,用户也会看到一致的错误响应,而不是直接暴露服务内部的错误。 以下是实现统一异常处理的基本步骤: 1. 配置全局错误处理器:在 `application.yml` 或 `application.properties` 文件中添加全局异常处理器的配置,例如: ```yaml error: path: /error handler: exception: my.error.handler ``` 2. 创建全局错误处理器类:定义一个实现了 `ReactiveErrorWebExceptionHandler` 或其子类的类,并实现 `handle` 方法处理不同类型的错误。 ```java @Bean public GlobalErrorExceptionHandler myErrorHandler() { return new MyErrorExceptionHandler(); } public class MyErrorExceptionHandler implements ReactiveErrorWebExceptionHandler { @Override public Mono<ServerResponse> handle(ServerRequest request, Throwable ex) { // 这里可以根据异常类型、状态码等返回定制化的错误响应 return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR) .bodyValue("An unexpected error occurred: " + ex.getMessage()); } } ``` 3. 异常处理策略:你可以选择只处理特定类型的异常,或者使用 `@ExceptionHandler` 注解来处理特定的 HTTP 状态码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值