Spring Cloud Gateway 修改响应数据

}

public int getRawStatusCode() {

return 0;

}

public MultiValueMap<String, ResponseCookie> getCookies() {

return null;

}

}

public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered {

private final ModifyResponseBodyGatewayFilterFactory.Config config;

public ModifyResponseGatewayFilter(ModifyResponseBodyGatewayFilterFactory.Config config) {

this.config = config;

}

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {

public Mono writeWith(Publisher<? extends DataBuffer> body) {

Class inClass = ModifyResponseGatewayFilter.this.config.getInClass();

Class outClass = ModifyResponseGatewayFilter.this.config.getOutClass();

MediaType originalResponseContentType = (MediaType)exchange.getAttribute(“original_response_content_type”);

HttpHeaders httpHeaders = new HttpHeaders();

httpHeaders.setContentType(originalResponseContentType);

ModifyResponseBodyGatewayFilterFactory.ResponseAdapter responseAdapter = ModifyResponseBodyGatewayFilterFactory.this.new ResponseAdapter(body, httpHeaders);

DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());

Mono modifiedBody = clientResponse.bodyToMono(inClass).flatMap((originalBody) -> {

return ModifyResponseGatewayFilter.this.config.rewriteFunction.apply(exchange, originalBody);

});

BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);

CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getResponse().getHeaders());

return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {

long contentLength1 = this.getDelegate().getHeaders().getContentLength();

Flux messageBody = outputMessage.getBody();

HttpHeaders headers = this.getDelegate().getHeaders();

if (!headers.containsKey(“Transfer-Encoding”)) {

messageBody = messageBody.doOnNext((data) -> {

headers.setContentLength((long)data.readableByteCount());

});

}

return this.getDelegate().writeWith(messageBody);

}));

}

public Mono writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {

return this.writeWith(Flux.from(body).flatMapSequential(§ -> {

return p;

}));

}

};

return chain.filter(exchange.mutate().response(responseDecorator).build());

}

public int getOrder() {

return -2;

}

}

}

复制代码

实现代码

====

根据源码示例,可以在网关过滤器添加类似逻辑,实现修改响应数据。

ResponseFilter


@Component

@Slf4j

public class ResponseFilter implements GlobalFilter, Ordered {

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

ServerHttpRequest request = exchange.getRequest();

URI uri = request.getURI();

String url = uri.getPath();

HttpStatus statusCode = exchange.getResponse().getStatusCode();

if(Objects.equals(statusCode, HttpStatus.BAD_REQUEST) || Objects.equals(statusCode, HttpStatus.TOO_MANY_REQUESTS)){

// 如果是特殊的请求,已处理响应内容,这里不再处理

return chain.filter(exchange);

}

// 根据具体业务内容,修改响应体

return modifyResponseBody(exchange, chain);

}

/**

  • 修改响应体

  • @param exchange

  • @param chain

  • @return

*/

private Mono modifyResponseBody(ServerWebExchange exchange, GatewayFilterChain chain) {

ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {

public Mono writeWith(Publisher<? extends DataBuffer> body) {

MediaType originalResponseContentType = (MediaType)exchange.getAttribute(“original_response_content_type”);

HttpHeaders httpHeaders = new HttpHeaders();

AtomicBoolean isHttpCodeOK = new AtomicBoolean(true);

httpHeaders.setContentType(originalResponseContentType);

ResponseAdapter responseAdapter = new ResponseAdapter(body, httpHeaders);

HttpStatus statusCode = this.getStatusCode();

// 修改后的响应体

Mono modifiedBody = getModifiedBody(statusCode, isHttpCodeOK, responseAdapter, exchange);

// 业务上的开关,表示是否开启加密,如果开启,就需要修改响应体,将响应体数据加密。开关从上下文获取。这里只关心是一个boolean值即可。

Boolean flag;

BodyInserter bodyInserter;

if(!flag) {

// 不需要修改响应数据

bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);

}else {

// 需要修改响应数据

// 这里最后采用了 ByteArrayResource 类去处理

bodyInserter = BodyInserters.fromPublisher(modifiedBody, ByteArrayResource.class);

}

CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, httpHeaders);

return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {

Flux messageBody = outputMessage.getBody();

ServerHttpResponse httpResponse = this.getDelegate();

HttpHeaders headers = httpResponse.getHeaders();

if (!headers.containsKey(“Transfer-Encoding”)) {

messageBody = messageBody.doOnNext((data) -> {

headers.setContentLength((long)data.readableByteCount());

});

}

if(!isHttpCodeOK.get()){

// 业务处理不是200,说明有异常,设置httpCode状态码是500

httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);

}

return httpResponse.writeWith(messageBody);

}));

}

public Mono writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {

return this.writeWith(Flux.from(body).flatMapSequential(§ -> {

return p;

}));

}

};

return chain.filter(exchange.mutate().response(responseDecorator).build());

}

public class ResponseAdapter implements ClientHttpResponse {

private final Flux flux;

private final HttpHeaders headers;

public ResponseAdapter(Publisher<? extends DataBuffer> body, HttpHeaders headers) {

this.headers = headers;

if (body instanceof Flux) {

this.flux = (Flux)body;

} else {

this.flux = ((Mono)body).flux();

}

}

public Flux getBody() {

return this.flux;

}

public HttpHeaders getHeaders() {

return this.headers;

}

public HttpStatus getStatusCode() {

return null;

}

public int getRawStatusCode() {

return 0;

}

public MultiValueMap<String, ResponseCookie> getCookies() {

return null;

}

}

@Override

public int getOrder() {

return FilterOrderConstant.getOrder(this.getClass().getName());

}

private Mono getModifiedBody(HttpStatus httpStatus, AtomicBoolean isHttpCodeOK, ResponseAdapter responseAdapter, ServerWebExchange exchange){

switch (httpStatus){

// 业务上需要特殊处理的状态码

case BAD_REQUEST:

case METHOD_NOT_ALLOWED:

isHttpCodeOK.set(false);

return getMono(HttpCode.BAD_REQUEST, exchange);

case INTERNAL_SERVER_ERROR:

isHttpCodeOK.set(false);

return getMono(HttpCode.SERVER_ERROR, exchange);

default:

// 主要处理流程

return getNormalBody(isHttpCodeOK, responseAdapter, exchange);

}

}

private Mono getNormalBody(AtomicBoolean isHttpCodeOK, ResponseAdapter responseAdapter, ServerWebExchange exchange){

DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());

return clientResponse.bodyToMono(String.class).flatMap((originalBody) -> {

// 业务上的开关,表示是否开启加密,如果开启,就需要修改响应体,将响应体数据加密。开关从上下文获取。这里只关心是一个boolean值即可。

Boolean flag;

ObjectMapper objectMapper = new ObjectMapper();

try {

R r = objectMapper.readValue(originalBody, R.class);

/**

  • 异常处理流程

*/

if(!r.getCode().equals(HttpCode.SUCCESS.getCode())){

// 业务处理不是200,说明有异常,直接返回对应错误

isHttpCodeOK.set(false);

ErrorR errorR = new ErrorR()

.setCode(r.getCode())

.setMsg(r.getMsg());

String json = objectMapper.writeValueAsString(errorR);

log.info(“json = {}”, json);

if(!flag) {

// 不需要加密,则不修改响应体

return Mono.just(json);

}else {

// 对返回数据进行加密 EncryptionUtil.encrypt(json, key)

// 具体加密逻辑不再阐述,这里可以理解成string 转成 byte[] 处理

byte[] encrypt = EncryptionUtil.encrypt(“{}”, key);

ByteArrayResource byteArrayResource = new ByteArrayResource(encrypt);

// 修改响应体,使用 byteArrayResource 封装

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后的内容

在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个40k,没做准备只有30k+,你们懂那种感觉吗)

如何准备面试?

1、前期铺垫(技术沉积)

程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。

下面是我会看的一些资料笔记,希望能帮助大家由浅入深,由点到面的学习Java,应对大厂面试官的灵魂追问

这部分内容过多,小编只贴出部分内容展示给大家了,见谅见谅!

  • Java程序员必看《Java开发核心笔记(华山版)》

  • Redis学习笔记

  • Java并发编程学习笔记

四部分,详细拆分并发编程——并发编程+模式篇+应用篇+原理篇

  • Java程序员必看书籍《深入理解 ava虚拟机第3版》(pdf版)

  • 大厂面试必问——数据结构与算法汇集笔记

其他像Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s等等我都整理好,这里就不一一展示了。

2、狂刷面试题

技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。

①大厂高频45道笔试题(智商题)

②BAT大厂面试总结(部分内容截图)

③面试总结

3、结合实际,修改简历

程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:

以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
③面试总结

[外链图片转存中…(img-KFqMjX03-1712727002489)]

[外链图片转存中…(img-GpstnrQ9-1712727002489)]

3、结合实际,修改简历

程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:

[外链图片转存中…(img-KGxfuUTT-1712727002489)]

以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 29
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值