• 从0到1学SpringCloud——14 gateway 获取请求报文RequestBody


    目录

    一、前言

    二、代码实现

    1、参考源码

    2、自定义请求报文解析

    3、自定义请求报文对象

    4、使用RouteLocator路由


    一、前言

    gateway使用webflux,底层使用异步非阻塞IO模型,在获取请求报文信息时,经常为null。

    本篇主要讲解以下两个方面:

    1、如何异步获取请求报文

    2、自定义请求报文数据类型

    二、代码实现

    1、参考源码

    关于异步获取请求报文的代码,官方提供了参考:ReadBodyRoutePredicateFactory.java

    1. public class ReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory {
    2. protected static final Log log = LogFactory.getLog(ReadBodyRoutePredicateFactory.class);
    3. private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute";
    4. private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
    5. private final List> messageReaders;
    6. public ReadBodyRoutePredicateFactory() {
    7. super(ReadBodyRoutePredicateFactory.Config.class);
    8. this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
    9. }
    10. public ReadBodyRoutePredicateFactory(List> messageReaders) {
    11. super(ReadBodyRoutePredicateFactory.Config.class);
    12. this.messageReaders = messageReaders;
    13. }
    14. public AsyncPredicate applyAsync(ReadBodyRoutePredicateFactory.Config config) {
    15. return new AsyncPredicate() {
    16. public Publisher apply(ServerWebExchange exchange) {
    17. Class inClass = config.getInClass();
    18. Object cachedBody = exchange.getAttribute("cachedRequestBodyObject");
    19. if (cachedBody != null) {
    20. try {
    21. boolean test = config.predicate.test(cachedBody);
    22. exchange.getAttributes().put("read_body_predicate_test_attribute", test);
    23. return Mono.just(test);
    24. } catch (ClassCastException var6) {
    25. if (ReadBodyRoutePredicateFactory.log.isDebugEnabled()) {
    26. ReadBodyRoutePredicateFactory.log.debug("Predicate test failed because class in predicate does not match the cached body object", var6);
    27. }
    28. return Mono.just(false);
    29. }
    30. } else {
    31. return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> {
    32. return ServerRequest.create(exchange.mutate().request(serverHttpRequest).build(), ReadBodyRoutePredicateFactory.this.messageReaders).bodyToMono(inClass).doOnNext((objectValue) -> {
    33. exchange.getAttributes().put("cachedRequestBodyObject", objectValue);
    34. }).map((objectValue) -> {
    35. return config.getPredicate().test(objectValue);
    36. });
    37. });
    38. }
    39. }
    40. public String toString() {
    41. return String.format("ReadBody: %s", config.getInClass());
    42. }
    43. };
    44. }
    45. public Predicate apply(ReadBodyRoutePredicateFactory.Config config) {
    46. throw new UnsupportedOperationException("ReadBodyPredicateFactory is only async.");
    47. }
    48. public static class Config {
    49. private Class inClass;
    50. private Predicate predicate;
    51. private Map hints;
    52. public Config() {
    53. }
    54. public Class getInClass() {
    55. return this.inClass;
    56. }
    57. public ReadBodyRoutePredicateFactory.Config setInClass(Class inClass) {
    58. this.inClass = inClass;
    59. return this;
    60. }
    61. public Predicate getPredicate() {
    62. return this.predicate;
    63. }
    64. public ReadBodyRoutePredicateFactory.Config setPredicate(Predicate predicate) {
    65. this.predicate = predicate;
    66. return this;
    67. }
    68. public ReadBodyRoutePredicateFactory.Config setPredicate(Class inClass, Predicate predicate) {
    69. this.setInClass(inClass);
    70. this.predicate = predicate;
    71. return this;
    72. }
    73. public Map getHints() {
    74. return this.hints;
    75. }
    76. public ReadBodyRoutePredicateFactory.Config setHints(Map hints) {
    77. this.hints = hints;
    78. return this;
    79. }
    80. }
    81. }

    源码中提供了异步获取方法、断言规则、对象类型,根据这里的逻辑,我们可以自定义一个解析j规则。

    2、自定义请求报文解析

    继承AbstractRoutePredicateFactory,自定义一个json请求报文解析工厂:

    ZhufengJsonReadBodyRoutePredicateFactory.java

    1. /**
    2. * @ClassName: ZhufengJsonRoutePredicateFactory
    3. * @Description 解析json请求
    4. * @author 月夜烛峰
    5. * @date 2022/9/14 19:52
    6. */
    7. @Slf4j
    8. @Component
    9. public class ZhufengJsonReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory {
    10. private static final String JSON_ATTRIBUTE = "msg_type_predicate_json_attribute";
    11. private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedMsgTypeObject";
    12. private final List> messageReaders;
    13. public ZhufengJsonReadBodyRoutePredicateFactory() {
    14. super(Config.class);
    15. this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
    16. }
    17. public ZhufengJsonReadBodyRoutePredicateFactory(List> messageReaders) {
    18. super(Config.class);
    19. this.messageReaders = messageReaders;
    20. }
    21. @Override
    22. public ShortcutType shortcutType() {
    23. return ShortcutType.GATHER_LIST;
    24. }
    25. @Override
    26. public List shortcutFieldOrder() {
    27. return Arrays.asList("patterns");
    28. }
    29. @Override
    30. public AsyncPredicate applyAsync(Config config) {
    31. return new AsyncPredicate() {
    32. @Override
    33. public Publisher apply(ServerWebExchange exchange) {
    34. Class inClass = JSONObject.class;
    35. Predicate predicate = msgInfo -> {
    36. log.info("请求数据:" + msgInfo);
    37. log.info("patterns:" + JSONObject.toJSONString(config.patterns));
    38. String msgtype = msgInfo.getString("msgType");
    39. if (config.patterns.contains(msgtype)) {
    40. log.info("验证成功");
    41. return true;
    42. }
    43. log.error("报文格式错误");
    44. return false;
    45. };
    46. Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
    47. log.info("打印属性cachedBody:" + cachedBody);
    48. if (cachedBody != null) {
    49. try {
    50. boolean test = predicate.test((JSONObject) cachedBody);
    51. exchange.getAttributes().put(JSON_ATTRIBUTE, test);
    52. return Mono.just(test);
    53. } catch (ClassCastException e) {
    54. log.error("Predicate test failed because class in predicate "
    55. + "does not match the cached body object", e);
    56. }
    57. return Mono.just(false);
    58. } else {
    59. return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
    60. (serverHttpRequest) -> ServerRequest
    61. .create(exchange.mutate().request(serverHttpRequest)
    62. .build(), messageReaders)
    63. .bodyToMono(inClass)
    64. .doOnNext(objectValue -> exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
    65. .map(objectValue -> predicate.test(objectValue)));
    66. }
    67. }
    68. @Override
    69. public String toString() {
    70. return String.format("ReadBody: %s", String.class);
    71. }
    72. };
    73. }
    74. @Override
    75. public Predicate apply(Config config) {
    76. throw new UnsupportedOperationException(
    77. "ReadBodyPredicateFactory is only async.");
    78. }
    79. public static class Config {
    80. private List patterns = new ArrayList();
    81. public List getPatterns() {
    82. return patterns;
    83. }
    84. public Config setPatterns(List patterns) {
    85. this.patterns = patterns;
    86. return this;
    87. }
    88. }
    89. }

    在数据库新增请求报文的断言规则,断言代码和数据库结构可参考:

    从0到1学SpringCloud——12 gateway 动态配置网关路由规则

    新增数据:

    1. INSERT INTO `zf_gateway_route` (`route_id`, `uri`, `path_name`, `path_pattern`, `method_name`, `method_pattern`, `msg_name`, `msg_type`, `filter_name`, `new_path`, `remark`, `status`, `index`) VALUES ('zhufeng-body-json', 'lb://zhufeng-web-msg', 'Path', '/msg/json', 'Method', 'Post', 'ZhufengJsonReadBody', 'json', NULL, NULL, '路由测试', 0, 5);
    2. INSERT INTO `zf_gateway_route` (`route_id`, `uri`, `path_name`, `path_pattern`, `method_name`, `method_pattern`, `msg_name`, `msg_type`, `filter_name`, `new_path`, `remark`, `status`, `index`) VALUES ('zhufeng-body-user', 'lb://zhufeng-web-msg', 'Path', '/msg/user', 'Method', 'Post', 'ZhufengUserReadBody', 'user', NULL, NULL, '路由测试', 0, 5);

    请求测试报文:

    1. {
    2. "message": "json测试",
    3. "msgType":"json",
    4. "name": "yueyezhufeng"
    5. }

    当发送 Post 请求 /msg/json 路径时,如果请求报文中 msgType 值为 json ,则校验通过。

    控制台信息:

    3、自定义请求报文对象

    虽然请求可以正常获取,也可以正常校验,但是使用json格式容易引起误解,毕竟json格式的字符串一定场合也会解析,为了更好对比,我们定义一个UserInfo对象:

    1. /**
    2. * @ClassName: UserInfo
    3. * @Description 自定义对象
    4. * @author 月夜烛峰
    5. * @date 2022/9/16 18:12
    6. */
    7. public class UserInfo {
    8. private String userId;
    9. private String userName;
    10. public String getUserId() {
    11. return userId;
    12. }
    13. public void setUserId(String userId) {
    14. this.userId = userId;
    15. }
    16. public String getUserName() {
    17. return userName;
    18. }
    19. public void setUserName(String userName) {
    20. this.userName = userName;
    21. }
    22. }

    在刚才新增数据时,添加了两条数据,一个是json请求报文的,一个就是用来解析UserInfo。

    ZhufengUserReadBodyRoutePredicateFactory.java代码:

    1. /**
    2. * @ClassName: ZhufengUserReadBodyRoutePredicateFactory
    3. * @Description 自定义User报文体解析类
    4. * @author 月夜烛峰
    5. * @date 2022/9/18 12:16
    6. */
    7. @Slf4j
    8. @Component
    9. public class ZhufengUserReadBodyRoutePredicateFactory extends ReadBodyRoutePredicateFactory {
    10. private static final String ZF_USER_ATTRIBUTE = "read_body_predicate_user_attribute";
    11. private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
    12. private static final List> messageReaders = HandlerStrategies
    13. .withDefaults().messageReaders();
    14. public ZhufengUserReadBodyRoutePredicateFactory() {
    15. super();
    16. }
    17. @Override
    18. public AsyncPredicate applyAsync(ReadBodyRoutePredicateFactory.Config config) {
    19. return exchange -> {
    20. Predicate predicate = msgInfo -> {
    21. log.info("请求数据:" + msgInfo.getUserName());
    22. return true;
    23. };
    24. config.setPredicate(predicate);
    25. config.setInClass(UserInfo.class);
    26. log.info("打印属性formdata:{}",exchange.getFormData().toString());
    27. log.info("打印属性queryparam:{}",exchange.getRequest().getQueryParams());
    28. Class inClass = config.getInClass();
    29. Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
    30. if (cachedBody != null) {
    31. try {
    32. boolean test = config.getPredicate().test(cachedBody);
    33. exchange.getAttributes().put(ZF_USER_ATTRIBUTE, test);
    34. return Mono.just(test);
    35. } catch (ClassCastException e) {
    36. if (log.isDebugEnabled()) {
    37. log.debug("Predicate test failed because class in predicate "
    38. + "does not match the cached body object", e);
    39. }
    40. }
    41. return Mono.just(false);
    42. } else {
    43. return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> ServerRequest.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders).bodyToMono(inClass).doOnNext((objectValue) -> {
    44. exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue);
    45. }).map((objectValue) -> {
    46. UserInfo userInfo = (UserInfo)objectValue;
    47. log.info(userInfo.getUserId()+":"+userInfo.getUserName());
    48. return config.getPredicate().test(objectValue);
    49. }));
    50. }
    51. };
    52. }
    53. }

    通过restTemplate发起测试:

    1. @RequestMapping("postUser")
    2. public String postUser() {
    3. //请求参数,仅作为演示参数传递
    4. UserInfo user = new UserInfo();
    5. user.setUserId("zf1001");
    6. user.setUserName("月夜烛峰");
    7. //zhufeng-gateway-db 为访问的微服务名称
    8. String pref = "http://zhufeng-gateway-db";
    9. //uri为微服务中访问的地址
    10. String uri = "/msg/user";
    11. ResponseEntity res = restTemplate.postForEntity(pref+uri, user, UserInfo.class);
    12. return "postUser UserInfo";
    13. }

    User信息正常打印 

    4、使用RouteLocator路由

    如果使用RouteLocator可以指定请求报文的对象类型,如下:

    1. @Bean
    2. public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
    3. // 构建多个路由routes
    4. RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
    5. routes.route("zhufeng-route-msg",
    6. r -> r.readBody(UserInfo.class, requestBody -> {
    7. log.info("requestBody is {}", requestBody);
    8. return true;
    9. }).and().path("/msg/**").
    10. uri("lb://zhufeng-web-msg"));
    11. return routes.build();
    12. }

    RouteLocator使用过程参考:

    从0到1学SpringCloud——13 gateway RouteLocator配置路由规则

  • 相关阅读:
    Python爬虫数据存哪里|数据存储到文件的几种方式
    Linux内核(十六)Linux 内核态进行读写文件的函数 使用和解析
    .net程序员的android studio 初体验 (环境设置2022年10月)
    jar添加到本地maven仓库
    Unity--Configurable Joint——实战带你了解可配置关节
    指针与引用
    End-to-end 3D Human Pose Estimation with Transformer
    优化|优化处理可再生希尔伯特核空间的非参数回归中的协变量偏移
    webstorage
    【HTML】筑基篇
  • 原文地址:https://blog.csdn.net/yueyezhufeng/article/details/126947143