范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

SpringCloudGateway修改请求和响应body的内容

  欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos本篇概览作为《Spring Cloud Gateway实战》系列的第九篇,咱们聊聊如何用Spring Cloud Gateway修改原始请求和响应内容,以及修改过程中遇到的问题首先是修改请求body,如下图,浏览器是请求发起方,真实参数只有user-id,经过网关时被塞入字段user-name,于是,后台服务收到的请求就带有user-name字段了
  其次是修改响应,如下图,服务提供方 provider-hello 的原始响应只有 response-tag 字段,经过网关时被塞入了 gateway-response-tag 字段,最终浏览器收到的响应就是 response-tag 和 gateway-response-tag 两个字段:
  总的来说,今天要做具体事情如下:准备工作:在服务提供者的代码中新增一个web接口,用于验证Gateway的操作是否有效介绍修改请求body和响应body的套路按套路开发一个过滤器(filter),用于修改请求的body按套路开发一个过滤器(filter),用于修改响应的body思考和尝试:如何从Gateway返回错误?在实战过程中,咱们顺便搞清楚两个问题:代码配置路由时,如何给一个路由添加多个filter?代码配置路由和yml配置是否可以混搭,两者有冲突吗?源码下载本篇实战中的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
  这个git项目中有多个文件夹,本篇的源码在 spring-cloud-tutorials 文件夹下,如下图红框所示:
  spring-cloud-tutorials 文件夹下有多个子工程,本篇的代码是 gateway-change-body ,如下图红框所示:
  准备工作为了观察Gateway能否按预期去修改请求和响应的body,咱们给服务提供者 provider-hello 增加一个接口,代码在Hello.java中,如下:    @PostMapping("/change")     public Map change(@RequestBody Map map) {         map.put("response-tag", dateStr());         return map;     } 可见新增的web接口很简单:将收到的请求数据作为返回值,在里面添加了一个键值对,然后返回给请求方,有了这个接口,咱们就能通过观察返回值来判断Gateway对请求和响应的操作是否生效来试一下,先启动nacos(provider-hello需要的)再运行provider-hello应用,用Postman向其发请求试试,如下图,符合预期:
  准备工作已完成,开始开发吧 修改请求body的套路如何用Spring Cloud Gateway修改请求的body?来看看其中的套路:修改请求body是通过自定义filter实现的配置路由及其filter的时候,有yml配置文件和代码配置两种方式可以配置路由,官方文档给出的demo是代码配置的,因此今天咱们也参考官方做法,通过代码来配置路由和过滤器在代码配置路由的时候,调用filters方法,该方法的入参是个lambda表达式此lambda表达式固定调用modifyRequestBody方法,咱们只要定义好modifyRequestBody方法的三个入参即可modifyRequestBody方法的第一个入参是输入类型第二个入参是返回类型第三个是RewriteFunction接口的实现,这个代码需要您自己写,内容是将输入数据转换为返回类型数据具体逻辑,咱们来看官方Demo,也就是上述套路了:@Bean public RouteLocator routes(RouteLocatorBuilder builder) {     return builder.routes()         .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")             .filters(f -> f.prefixPath("/httpbin")                 .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,                     (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))         .build(); }修改响应body的套路用Spring Cloud Gateway修改响应body的套路和前面的请求body如出一辙通过代码来配置路由和过滤器在代码配置路由的时候,调用filters方法,该方法的入参是个lambda表达式此lambda表达式固定调用modifyResponseBody方法,咱们只要定义好modifyResponseBody方法的三个入参即可modifyRequestBody方法的第一个入参是输入类型第二个入参是返回类型第三个是RewriteFunction接口的实现,这个代码要您自己写,内容是将输入数据转换为返回类型数据具体逻辑,咱们来看官方Demo,其实就是上述套路:@Bean public RouteLocator routes(RouteLocatorBuilder builder) {     return builder.routes()         .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")             .filters(f -> f.prefixPath("/httpbin")                 .modifyResponseBody(String.class, String.class,                     (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))         .build(); } 套路总结出来了,接下来,咱们一起撸代码? 按套路开发一个修改请求body的过滤器(filter)废话不说,在父工程spring-cloud-tutorials下新建子工程gateway-change-body,pom.xml无任何特殊之处,注意依赖spring-cloud-starter-gateway即可启动类毫无新意:package com.bolingcavalry.changebody;  import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;  @SpringBootApplication public class ChangeBodyApplication {     public static void main(String[] args) {         SpringApplication.run(ChangeBodyApplication.class,args);     } }配置文件千篇一律: server:   #服务端口   port: 8081 spring:   application:     name: gateway-change-body然后是核心逻辑:修改请求body的代码,既RewriteFunction的实现类,代码很简单,将原始的请求body解析成Map对象,取出user-id字段,生成user-name字段放回map,apply方法返回的是个Mono: package com.bolingcavalry.changebody.function;  import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Map;   @Slf4j public class RequestBodyRewrite implements RewriteFunction {      private ObjectMapper objectMapper;      public RequestBodyRewrite(ObjectMapper objectMapper) {         this.objectMapper = objectMapper;     }      /**      * 根据用户ID获取用户名称的方法,可以按实际情况来内部实现,例如查库或缓存,或者远程调用      * @param userId      * @return      */     private  String mockUserName(int userId) {         return "user-" + userId;     }      @Override     public Publisher apply(ServerWebExchange exchange, String body) {         try {             Map map = objectMapper.readValue(body, Map.class);              // 取得id             int userId = (Integer)map.get("user-id");              // 得到nanme后写入map             map.put("user-name", mockUserName(userId));              // 添加一个key/value             map.put("gateway-request-tag", userId + "-" + System.currentTimeMillis());              return Mono.just(objectMapper.writeValueAsString(map));         } catch (Exception ex) {             log.error("1. json process fail", ex);             // json操作出现异常时的处理             return Mono.error(new Exception("1. json process fail", ex));         }     } }然后是按部就班的基于代码实现路由配置,重点是lambda表达式执行modifyRequestBody方法,并且将RequestBodyRewrite作为参数传入: package com.bolingcavalry.changebody.config;  import com.bolingcavalry.changebody.function.RequestBodyRewrite; import com.bolingcavalry.changebody.function.ResponseBodyRewrite; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import reactor.core.publisher.Mono;  @Configuration public class FilterConfig {     @Bean     public RouteLocator routes(RouteLocatorBuilder builder, ObjectMapper objectMapper) {         return builder                 .routes()                 .route("path_route_change",                         r -> r.path("/hello/change")                                 .filters(f -> f                                         .modifyRequestBody(String.class,String.class,new RequestBodyRewrite(objectMapper))                                         )                         .uri("http://127.0.0.1:8082"))                 .build();     } }代码写完了,运行工程 gateway-change-body ,在postman发起请求,得到响应如下图,红框中可见Gateway添加的内容已成功:
  现在修改请求body已经成功,接下来再来修改服务提供者响应的body 修改响应body接下来开发修改响应body的代码 新增RewriteFunction接口的实现类ResponseBodyRewrite.java package com.bolingcavalry.changebody.function;  import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Map;  @Slf4j public class ResponseBodyRewrite implements RewriteFunction {      private ObjectMapper objectMapper;      public ResponseBodyRewrite(ObjectMapper objectMapper) {         this.objectMapper = objectMapper;     }      @Override     public Publisher apply(ServerWebExchange exchange, String body) {         try {             Map map = objectMapper.readValue(body, Map.class);              // 取得id             int userId = (Integer)map.get("user-id");              // 添加一个key/value             map.put("gateway-response-tag", userId + "-" + System.currentTimeMillis());              return Mono.just(objectMapper.writeValueAsString(map));         } catch (Exception ex) {             log.error("2. json process fail", ex);             return Mono.error(new Exception("2. json process fail", ex));         }     } }路由配置代码中,lambda表达式里面,filters方法内部调用modifyResponseBody,第三个入参是ResponseBodyRewrite: package com.bolingcavalry.changebody.config;  import com.bolingcavalry.changebody.function.RequestBodyRewrite; import com.bolingcavalry.changebody.function.ResponseBodyRewrite; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import reactor.core.publisher.Mono;  @Configuration public class FilterConfig {      @Bean     public RouteLocator routes(RouteLocatorBuilder builder, ObjectMapper objectMapper) {         return builder                 .routes()                 .route("path_route_change",                         r -> r.path("/hello/change")                                 .filters(f -> f                                         .modifyRequestBody(String.class,String.class,new RequestBodyRewrite(objectMapper))                                         .modifyResponseBody(String.class, String.class, new ResponseBodyRewrite(objectMapper))                                         )                         .uri("http://127.0.0.1:8082"))                 .build();     } }还记得咱们的第一个问题吗?通过上面的代码,您应该已经看到了答案:用代码配置路由时,多个过滤器的配置方法就是在filters方法中反复调用内置的过滤器相关API,下图红框中的都可以:
  运行服务,用Postman验证效果,如下图红框,Gateway在响应body中成功添加了一个key&value:
  代码配置路由和yml配置是否可以混搭?前面有两个问题,接下来回答第二个,咱们在application.yml中增加一个路由配置: server:   #服务端口   port: 8081 spring:   application:     name: gateway-change-body   cloud:     gateway:       routes:         - id: path_route_str           uri: http://127.0.0.1:8082           predicates:             - Path=/hello/str把 gateway-change-body 服务启动起来,此时已经有了两个路由配置,一个在代码中,一个在yml中,先试试yml中的这个,如下图没问题:
  再试试代码配置的路由,如下图,结论是 代码配置路由和yml配置 可以 混搭
  如何处理异常还有个问题必须要面对:修改请求或者响应body的过程中,如果发现问题需要提前返回错误(例如必要的字段不存在),代码该怎么写? 咱们修改请求body的代码集中在RequestBodyRewrite.java,增加下图红框内容:
  再来试试,这次请求参数中不包含 user-id ,收到Gateway返回的错误信息如下图:
  看看控制台,能看到代码中抛出的异常信息:
  此时,聪明的您应该发现问题所在了:咱们想告诉客户端具体的错误,但实际上客户端收到的是被Gateway框架处理后的内容 篇幅所限,上述问题从分析到解决的过程,就留给下一篇文章吧 本篇的最后,请容许欣宸唠叨两句,聊聊为何要网关来修改请求和响应body的内容,如果您没兴趣还请忽略 网关(Gateway)为什么要做这些?看过开篇的两个图,聪明的您一定发现了问题:为什么要破坏原始数据,一旦系统出了问题如何定位是服务提供方还是网关?按照欣宸之前的经验,尽管网关会破坏原始数据,但只做一些简单固定的处理,一般以添加数据为主,网关不了解业务,最常见的就是鉴权、添加身份或标签等操作前面的图中确实感受不到网关的作用,但如果网关后面有多个服务提供者,如下图,这时候诸如鉴权、获取账号信息等操作由网关统一完成,比每个后台分别实现一次更有效率,后台可以更加专注于自身业务:
  经验丰富的您可能会对我的狡辩不屑一顾:网关统一鉴权、获取身份,一般会把身份信息放入请求的header中,也不会修改请求和响应的内容啊,欣宸前面的一堆解释还是没说清楚为啥要在网关位置修改请求和响应的内容!好吧,面对聪明的您,我摊牌了:本篇只是从技术上演示Spring Cloud Gateway如何修改请求和响应内容,请不要将此技术与实际后台业务耦合;欢迎关注头条号:程序员欣宸学习路上,你不孤单,欣宸原创一路相伴...

联想YOGAPro14s2023款官方预热,珠宝式外观设计近日,联想宣布YOGAPro系列YOGABook新品即将到来。现在,最新的官方预热中也显示了更多的产品细节信息。今天,联想YOGA预热称,你们心心念念的YOGAPro14s2023南唐时期,朝廷公文的具体概述生态研究可以说是研究南唐公文的一个独特的视角,从文牍学文化学历史学生态美学社会学角度对南唐公文进行研究具有重要意义。文章立足南唐公文文本,将生态研究的方法引入南唐公文研究过程中,从考第一也要被流放,明朝的考场有多严格?文白面书誏编辑白面书誏明朝的科举考试制度十分重要,因为通过科举考试可以使一个人跻身于社会上层。然而考试制度也容易出现舞弊和作弊行为,因此,明朝设立了相应的监察制度来保证考试的公正性明史杂谈明朝中后期的财政困局在阅读此文之前,麻烦您点击一下关注,既方便您进行讨论和分享,又能给您带来不一样的参与感,感谢您的支持。古代清朝的康熙皇帝曾经这样评价它隆治唐宋,现代人民大学教授毛佩琦曾说它是中国历富轩全屋门窗亮相2023中国建博会,引领高端门窗潮流2023中国建博会(上海)即将在上海虹桥国家会展中心二层三号馆隆重举办,届时富轩全屋门窗将参加此次盛会,展示我们的最新产品和技术。富轩全屋门窗一直致力于为消费者提供高品质高性能的门发挥好资本市场扩内需作用今年的政府工作报告将着力扩大国内需求放在重点工作之首,提出要把恢复和扩大消费摆在优先位置。资本市场集资本劳动力技术数据等关键要素于一身,具有高效配置资源合理分配收益的功能,在扩大内3月16日凌晨,中国传来5个消息,洪都拉斯想与中国建立外交关系今天是3月16日,新的一天,新的开始。祖国各地发生了很多事情,有的已经发生,有的即将发生,个人认为这几个消息非常给力,也很哇塞,有人为了生活弯下了腰,也有人为了事业努力拼搏,请大家3月16日凌晨,中国传来34个新消息,为什么总有人拿教师编制说事3月16日凌晨,中国传来34个新消息,为什么总有人拿教师编制说事?34教师退出机制,不允许老师躺平!对此,你们大家怎么看?网友1编制就是保证农村小城市孩子享受较好师资待遇的一个福利4年交往3000名男子,28岁跳海自杀,一生悲凉在现代社会,穿着暴露恋爱自由,都是很正常的。然后在西方思想和传统文化激烈碰撞的民国时代,很多的女性都没有地位,也没有选择自己婚姻的自由和权利。但是却有这样的一个女子,她与家族作对,从张耳陈余之情同父子的刎颈之交变为死敌看人性之可怕刎颈之交这个词起源于蔺相如与廉颇的故事。缘于蔺相如出使秦国立大功归赵以后,赵惠文王为酬功封他为上卿。这件事令让屡立战功身为上将的廉颇非常不满。为了折辱蔺相如,廉颇甚至扬言要当面羞辱大连理工大学是忘了历史的痛吗?还是国人忘了历史?贼人亡我之心无处不在,堂堂中国十四亿人民,到底又有多少汉奸走狗渗透,之前的毒教材,现在还是书本,国家教育部门应该好好的管一管了,再这样下去,汉奸走狗将无法无天了。去年就毒教材事件闹
你知道广东人的冰箱到底有多能装吗?我认识一个做粮油生意的广东老板,今年大概五十岁左右,身家过亿。平时一身休闲装,都是一些普通的牌子。家里有几辆车,都是几十万的小车,车尾箱放着两箱洋酒和几条中华。平时我们一班朋友出去捡烟头年入三十万,请问收烟头有何价值,为何有人愿意花钱回收?李大爷最近几年靠着捡烟头实现了年入五十万的理想,而且还给儿子贷款买了套婚房,并且每年都带老伴旅游两次,他老人家告诉我,烟头的价值不仅可以当做制造一些纺织品的用处,也可以再次回收利用那些年耐用的车有哪些,你目前这辆车用了多久呢?我目前这辆车是比亚迪速锐1。5L手挡的,14年底的车,还出过一次事故,当时跟一辆老款吉利撞了,我还在头条记录了事故过程,有兴趣的可以点我主页看看。到今天已经7年的车了,还算皮实耐用可以推荐个准确度较高的万用表吗?想买个万用表,哪个牌子准确度高点,谢谢各位朋友?对于电工的朋友来说,万用表方便实用必不可少。很多朋友在选万用表的时候都觉得准确度比较重要,甚至要求万用表要千分之几的精确度。其实我觉闲置不用的面包机有哪些其他作用?就我家的情况来说,面包机自从买来就没有闲置的。我已经尝试做了N多种面包了,各种面包极大的丰富了我家的食谱。比如早餐面包下午茶面包零食面包,连坐火车的时候我也做了个面包带着去充当午餐脸上雀斑多,怎么化妆遮盖?脸上的雀斑多,化妆除了认真的清洗,做好皮肤清洁,护理,隔离。包括用洗面奶清洁,相应的精华或者面霜的护理后,做好隔离,这是基础的化妆前奏。化妆就是要让自己更自信,更漂亮。想取得效果最古今中外你听过最残忍的案件是哪一件?老板向顾客多要一元钱,顾客剁下老板的脑袋,要了老板的一条命。老板想不到,自己磨的锋利的菜刀,会如砍瓜切菜般地剁下他的脑袋。案件发生后,当地警方接到的报警,竟多达50多个!林明是一个陈亚男与朱之文相互爆料,谁会是赢家?会不会被封杀?朱小伟的不作为,使自己的家庭占用网络公共资源这么久,大衣哥作为长辈应该知道儿子的斤两,就应该迅速起到长辈的作用,而选择任意由网络炒作,是不符责任的态度。陈亚楠的道歉信没有得到回复,不是肇俊哲,不是陈婉婷,为什么不能是刘华娜或王军?主教练,是球队的统帅,灵魂,就应该象指挥古战场千军万马的主帅,其团队要懂兵法才行,头脑简单四肢发过的不能说是足球内行,只能免强说会踢球的内行,不能算是教练内行,古今中外那么多优秀的俗话说男人看鞋,女人看走姿,是什么意思?这个说法精辟!从一个男人的鞋基本可以看懂他这个人。比如他的经济状况文化程度职业审美兴趣爱好甚至他的性格特征等等。可以说鞋是男人的品味阶层的集中体现。女人看走姿。说的是,走姿是女人内公职人员能享受农村拆迁补偿吗?我认为公职人员应该分成两部分,一部分是自己已经在农村老家结婚成家,有自己的房屋,自己的土地,有妻子儿女(或丈夫儿女)在家务农,后来经过自己努力或者其他原因参加了工作,成了公职人员,