写点什么

Spring Cloud Gateway 修改请求和响应 body 的内容

作者:Java高工P7
  • 2021 年 11 月 11 日
  • 本文字数:3553 字

    阅读完需:约 12 分钟

.modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VAL


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


UE,


(exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))


.build();


}

修改响应 body 的套路

  • 用 Spring Cloud Gateway 修改响应 body 的套路和前面的请求 body 如出一辙


  1. 通过代码来配置路由和过滤器

  2. 在代码配置路由的时候,调用 filters 方法,该方法的入参是个 lambda 表达式

  3. 此 lambda 表达式固定调用 modifyResponseBody 方法,咱们只要定义好 modifyResponseBody 方法的三个入参即可

  4. modifyRequestBody 方法的第一个入参是输入类型

  5. 第二个入参是返回类型

  6. 第三个是 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<String, String> {


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<String> apply(ServerWebExchange exchange, String body) {


try {


Map<String, Object> 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<String, String> {


private ObjectMapper objectMapper;


public ResponseBodyRewrite(ObjectMapper objectMapper) {


this.objectMapper = objectMapper;


}


@Override


public Publisher<String> apply(ServerWebExchange exchange, String body) {


try {


Map<String, Object> 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

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Spring Cloud Gateway修改请求和响应body的内容