摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览作为实战系列的第九篇,咱们聊聊如何用修改原始请求和响应内容,以及修改过程中遇到的问题首先是修改请求,如下图,浏览器是请求发起方,真实参数只有,经过网关时被塞
https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
作为《Spring Cloud Gateway实战》系列的第九篇,咱们聊聊如何用Spring Cloud Gateway修改原始请求和响应内容,以及修改过程中遇到的问题
首先是修改请求body,如下图,浏览器是请求发起方,真实参数只有user-id,经过网关时被塞入字段user-name,于是,后台服务收到的请求就带有user-name字段了
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
@PostMapping("/change") public Map change(@RequestBody Map map) { map.put("response-tag", dateStr()); return map; }
可见新增的web接口很简单:将收到的请求数据作为返回值,在里面添加了一个键值对,然后返回给请求方,有了这个接口,咱们就能通过观察返回值来判断Gateway对请求和响应的操作是否生效
来试一下,先启动nacos(provider-hello需要的)
再运行provider-hello应用,用Postman向其发请求试试,如下图,符合预期:
@Beanpublic 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();}
@Beanpublic 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();}
废话不说,在父工程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;@SpringBootApplicationpublic class ChangeBodyApplication { public static void main(String[] args) { SpringApplication.run(ChangeBodyApplication.class,args); }}
server: #服务端口 port: 8081spring: application: name: gateway-change-body
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;@Slf4jpublic 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)); } }}
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;@Configurationpublic 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(); }}
接下来开发修改响应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;@Slf4jpublic 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)); } }}
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;@Configurationpublic 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(); }}
server: #服务端口 port: 8081spring: application: name: gateway-change-body cloud: gateway: routes: - id: path_route_str uri: http://127.0.0.1:8082 predicates: - Path=/hello/str
还有个问题必须要面对:修改请求或者响应body的过程中,如果发现问题需要提前返回错误(例如必要的字段不存在),代码该怎么写?
咱们修改请求body的代码集中在RequestBodyRewrite.java,增加下图红框内容:
此时,聪明的您应该发现问题所在了:咱们想告诉客户端具体的错误,但实际上客户端收到的是被Gateway框架处理后的内容
篇幅所限,上述问题从分析到解决的过程,就留给下一篇文章吧
本篇的最后,请容许欣宸唠叨两句,聊聊为何要网关来修改请求和响应body的内容,如果您没兴趣还请忽略
看过开篇的两个图,聪明的您一定发现了问题:为什么要破坏原始数据,一旦系统出了问题如何定位是服务提供方还是网关?
按照欣宸之前的经验,尽管网关会破坏原始数据,但只做一些简单固定的处理,一般以添加数据为主,网关不了解业务,最常见的就是鉴权、添加身份或标签等操作
前面的图中确实感受不到网关的作用,但如果网关后面有多个服务提供者,如下图,这时候诸如鉴权、获取账号信息等操作由网关统一完成,比每个后台分别实现一次更有效率,后台可以更加专注于自身业务:
经验丰富的您可能会对我的狡辩不屑一顾:网关统一鉴权、获取身份,一般会把身份信息放入请求的header中,也不会修改请求和响应的内容啊,欣宸前面的一堆解释还是没说清楚为啥要在网关位置修改请求和响应的内容!
好吧,面对聪明的您,我摊牌了:本篇只是从技术上演示Spring Cloud Gateway如何修改请求和响应内容,请不要将此技术与实际后台业务耦合;
微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...
https://github.com/zq2599/blog_demos
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/124815.html
摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览作为实战系列的第五篇,是时候了解过滤器的作用了,本篇咱们一起来了解内置好的过滤器,真是种类繁多功能强大过滤器顾名思义,就是在请求头部添加指定的内容带有的完整配欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Doc...
摘要:单服务我们简单编写一个请求地址,输出字符串信息,添加依赖如下所示配置文件如下所示服务名注册到服务端口号配置该服务的服务名称为,这里对应的。 在上一篇文章Spring Cloud GateWay 路由转发规则介绍中我们讲解了SpringCloud Gateway内部提供的断言、谓语,让我们可以组合更精确的业务场景进行请求,既然SpringCloud GateWay担任了网关的角色,在之前...
摘要:组合示例相同的也可以配置多个,请求的转发是必须满足所有的后才可以进行路由转发,组合使用示例如下所示总结本章节讲解了的相关谓词断言基本使用方式,内部提供了很多种灵活的路由转发规则,在同一个路由内存在多个时,同时满足规则后请求才会被路由转发。 Spring在因Netflix开源流产事件后,在不断的更换Netflix相关的组件,比如:Eureka、Zuul、Feign、Ribbon等,Zuu...
摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览本文是实战系列的第八篇,经过前面的学习,咱们对过滤器已了解得差不多,今天来补全过滤器的最后一个版块限流默认的限流器是基于实现的,限流算法是大家熟悉的令牌桶关于欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Doc...
阅读 3366·2021-11-24 09:39
阅读 1778·2021-11-17 09:33
阅读 3434·2021-10-12 10:12
阅读 4953·2021-09-22 15:51
阅读 1097·2019-08-30 13:11
阅读 3551·2019-08-30 10:59
阅读 546·2019-08-30 10:48
阅读 1296·2019-08-26 13:48