基于RequestResponseBodyMethodProcessor的Trim功能装饰者模式实现

文章目录

  • 前言
  • 一、实现
    • 1.1 @Trim
    • 1.2 TrimRequestResponseBodyMethodProcessorDecorator
    • 1.3 Configuration
  • 二、测试
    • 2.1 测试用例
    • 2.2 测试结果
      • 2.2.1 Test no.1
      • 2.2.2 Test no.2
      • 2.2.3 Test no.3
      • 2.2.4 Test no.4


前言

公司内部系统老是有人填表单复制粘贴老是整出前后空格来.

前端项目烂尾, 考虑在服务端增加统一的trim处理.


一、实现

1.1 @Trim

  • 基于hutools的StrUtil.trim(value,mode)方法
  • 注解在controller方法参数上时作为开关
/*** Based on {@code cn.hutool.core.util.StrUtil.trim(value, mode)}* <p>* Usage:* <p>* In order to activate the processing, add the annotation on parameters* that were also annotated with {@code @RequestBody}. This will trigger* recursive checking of the fields in the parameters.* <p>* To trim specific fields, annotate on that field.* <p>* If the field is not a String type, the fields to be trimmed in the* object field also have to be annotated with {@code @Trim}.** <pre>* public MODIFIER method({@code @RequestBody @Trim }ParamClass){}** class ParamClass {*      {@code @Trim}*      private String trimmingField;**      {@code @Trim}*      private NoneStringField nonStringField;* }** class NoneStringField {*      {@code @Trim}*      private String nonStringTrimmingField;* }* </pre>** @author hp* @see com.luban.common.base.http.servlet.TrimRequestResponseBodyMethodProcessorDecorator*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface Trim {Mode value() default Mode.ALL;@Getter@AllArgsConstructorenum Mode implements BaseEnum<Mode, Integer> {/***/END(1, "trimEnd"), ALL(0, "trimAll"), START(-1, "trimStart"),;private final Integer code;private final String name;}
}

1.2 TrimRequestResponseBodyMethodProcessorDecorator

  • 装饰者模式实现, 保留功能同时增强自定义trim处理
  • trim目前固定基于hutools, 扩展自定义可以调整为提供一个facade设计注入这个类来提供具体的trim功能
 
/*** Replace RequestResponseBodyMethodProcessor or add this decorator before it.* <p>* Consider this example of configuring the Decorator** <pre>** {@code @RequiredArgsConstructor}* {@code @Configuration}* public class HandlerMethodArgumentResolverAutoConfiguration {**     private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;*     private final List<HttpMessageConverter<?>> converters;**     {@code @PostConstruct}*     public void setRequestExcelArgumentResolver() {*         List<HandlerMethodArgumentResolver> argumentResolvers = this.requestMappingHandlerAdapter.getArgumentResolvers();*         List<HandlerMethodArgumentResolver> resolverList = new ArrayList<>();*         resolverList.add(new TrimRequestResponseBodyMethodProcessorDecorator(new RequestResponseBodyMethodProcessor(converters)));*         assert argumentResolvers != null;*         resolverList.addAll(argumentResolvers);*         this.requestMappingHandlerAdapter.setArgumentResolvers(resolverList);*     }* }** </pre>** @author hp* @see RequestMappingHandlerAdapter*/
public class TrimRequestResponseBodyMethodProcessorDecorator implements HandlerMethodArgumentResolver {private final RequestResponseBodyMethodProcessor processor;public TrimRequestResponseBodyMethodProcessorDecorator(@NonNull RequestResponseBodyMethodProcessor processor) {this.processor = processor;}@Overridepublic boolean supportsParameter(@NonNull MethodParameter parameter) {return processor.supportsParameter(parameter);}@Overridepublic Object resolveArgument(@NonNull MethodParameter parameter, ModelAndViewContainer mavContainer, @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {final Object o = processor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);if (Objects.isNull(o)) {return parameter.isOptional() ? Optional.empty() : null;}// 开关parameter = parameter.nestedIfOptional();if (!parameter.hasParameterAnnotation(Trim.class)) {return o;}// 拿到真实数据对象, 因为原生支持Optional封装Object object;if (parameter.isOptional()) {final Optional<?> optional = (Optional<?>) o;assert optional.isPresent();object = optional.get();} else {object = o;}// 范型情况, 找到真实的对象Class<?> targetClass;if (parameter.getNestedGenericParameterType() instanceof Class<?> clazz) {targetClass = clazz;} else {ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);targetClass = resolvableType.resolve();}if (Objects.isNull(targetClass)) {return o;}trimObject(targetClass, object);return o;}private static void trimObject(Class<?> targetClass, Object object) {if (Objects.isNull(targetClass) || targetClass == Object.class) {return;}ReflectionUtils.doWithFields(targetClass,field -> {final Trim fieldTrim = field.getAnnotation(Trim.class);assert fieldTrim != null;if (field.getType() == String.class) {ReflectionUtils.makeAccessible(field);final String stringVal = (String) field.get(object);final String trimmedVal = StrUtil.trim(stringVal, fieldTrim.value().getCode());field.set(object, trimmedVal);} else {ReflectionUtils.makeAccessible(field);final Object value = field.get(object);// 从实际对象取, 避免范型问题final Class<?> fieldClass = value.getClass();trimObject(fieldClass, value);}},field -> (AnnotatedElementUtils.hasAnnotation(field, Trim.class)));}
}

1.3 Configuration

  • 1.1 仅实现请求参数处理, 响应未实现, 所以配置上仅配置参数处理器即可, 添加到原生处理器之前即可
@RequiredArgsConstructor
@Configuration
public class HandlerMethodArgumentResolverAutoConfiguration {private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;private final List<HttpMessageConverter<?>> converters;@PostConstructpublic void setRequestExcelArgumentResolver() {List<HandlerMethodArgumentResolver> argumentResolvers = this.requestMappingHandlerAdapter.getArgumentResolvers();List<HandlerMethodArgumentResolver> resolverList = new ArrayList<>();resolverList.add(new TrimRequestResponseBodyMethodProcessorDecorator(new RequestResponseBodyMethodProcessor(converters)));assert argumentResolvers != null;resolverList.addAll(argumentResolvers);this.requestMappingHandlerAdapter.setArgumentResolvers(resolverList);}
}

二、测试

2.1 测试用例

  • RequestResponseBodyMethodProcessor 翻了下源码, 支持Optional封装, 所以用例也测试一下这种场景
@Data
public static class JsonPayload {@Trim(Trim.Mode.START)private String data;
}@Data
public static class JsonPayload2 {@Trimprivate JsonPayload data;
}@PostMapping("/json")
public Returns<String> json(@RequestBody @Trim JsonPayload jsonPayload) {return Returns.success(jsonPayload.data);
}@PostMapping("/json2")
public Returns<String> json2(@RequestBody @Trim Optional<JsonPayload> jsonPayload) {if (jsonPayload.isEmpty()) {return Returns.fail();}return Returns.success(jsonPayload.get().data);
}@PostMapping("/json3")
public Returns<String> json3(@RequestBody @Trim JsonPayload2 jsonPayload) {return Returns.success(jsonPayload.data.data);
}@PostMapping("/json4")
public Returns<String> json4(@RequestBody @Trim Optional<JsonPayload2> jsonPayload) {if (jsonPayload.isEmpty()) {return Returns.fail();}return Returns.success(jsonPayload.get().data.data);
}

2.2 测试结果

2.2.1 Test no.1

# request
POST http://localhost:9999/json
Content-Type: application/json{"data": " 123 "
}
# response
{"code": 200,"message": "操作成功","data": "123 "
}

2.2.2 Test no.2

  • Optional包装
# request
POST http://localhost:9999/json2
Content-Type: application/json{"data": " 123 "
}
# response
{"code": 200,"message": "操作成功","data": "123 "
}

2.2.3 Test no.3

  • 嵌套对象
  • 外层对象注解属性trim两头, 但是内部string属性覆盖为trim开头
# request
POST http://localhost:9999/json3
Content-Type: application/json{"data": {"data": " 123 "}
}
# response
{"code": 200,"message": "操作成功","data": "123 "
}

2.2.4 Test no.4

  • Optional包装 + 嵌套对象
# request
POST http://localhost:9999/json4
Content-Type: application/json{"data": {"data": " 321 "}
}
# response
{"code": 200,"message": "操作成功","data": "321 "
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/13327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

摸鱼大数据——大数据导论

大数据导论 1、概念 大数据时代: 万物皆数据 ​ 数据概念: 人类的行为及产生的事件的一种记录称之为数据 ​ 数据价值: 对数据的内容进行深入分析&#xff0c;可以更好的帮助了解事和物在现实世界的运行规律 2、大数据诞生 大数据的诞生: 跟随着互联网的发展的,当全球互联…

K8S认证 | CKA题库 + 答案 | 查看Pod CPU资源使用量

2、查看集群中运行Pod CPU资源使用量 您必须在以下Cluster/Node上完成此考题&#xff1a; Cluster Master node Worker node k8s …

【信息系统项目管理师知识点速记】采购管理:实施采购

实施采购过程是项目管理中一个关键环节,旨在通过一系列步骤确保项目所需的货物、服务或成果能够从合适的供应商处获得,并以合同形式确立双方的权利与义务。这一过程不仅关乎选择最合适的卖方,还包括了风险管理、成本控制、沟通规划等多个方面的考量。 输入概述 项目管理计划…

从零开始精通RTSP之加密

概述 原始的RTSP通信默认使用的是明文传输&#xff0c;这也就意味着&#xff0c;在网络上的任何节点都能轻易地查看或修改传输的内容。这在涉及隐私或版权保护的场景下&#xff0c;是完全不可接受的。因此&#xff0c;加密显得尤为重要。加密的目的主要有三点&#xff1a;一是进…

拧紧设备或工具的选型原则、常见类型和选型关键因素有哪些?

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 在工业生产领域&#xff0c;拧紧控制是一项至关重要的工艺环节。它涉及螺栓、螺母等紧固件的装配过程&#xff0c;直接关系到产品的质量和性能。因此&#xff0c;选择合适的拧紧设备或工具对于确保拧紧过程的准确性和稳…

[牛客网]——C语言刷题day4

答案&#xff1a;B 解析&#xff1a; a是数组首元素的地址,a1的步长是int&#xff0c;所以是2 &a是数组的首地址&#xff0c;&a1步长是int[5] ptr是int类型的指针&#xff0c;指向a数组的尾后位置&#xff0c;ptr-1的步长是int,所以是a数组的最后一个元素5 答案&am…

如何使用ffmpeg 实现10种特效

相关特效的名字 特效id 特效名 1 向上移动 2 向左移动 3 向下移动 4 颤抖 5 摇摆 6 雨刷 7 弹入 8 弹簧 9 轻微跳动 10 跳动 特效展示(同时汇总相关命令) pad背景显示 pad背景透明 相关命令(一会再讲这些命令&#xff0c;先往下看) # 合成特效语音 ffmpeg -y -loglevel erro…

Element Plus/vue3 无限级导航实现

在使用element plus 时&#xff0c;最初要使用的就是导航组件了&#xff0c;官网上看到的也就是写死的一级/二级导航&#xff0c;那么如何设计一个无限级且动态的导航呢&#xff1f;毋庸置疑&#xff0c;递归。废话不多说&#xff0c;直接看代码和效果&#xff1a; 代码&#x…

Jmeter:录制脚本(操作文档)

新建线程组 打开jmeter&#xff0c;右键测试计划–>添加–>Threads(Users)–>点击"线程组" 添加录制控制器 右键线程组–>添加–>逻辑控制器–>点击"录制控制器" 添加HTTP代理服务器 右键工作台–>添加–>非测试元件–>…

YOLOv5改进 | Neck | 添加双向特征金字塔BiFPN【小白轻松上手 | 论文必备】

&#x1f680;&#x1f680;&#x1f680;本专栏所有的改进均可成功执行&#x1f680;&#x1f680;&#x1f680; 尽管Ultralytics 推出了最新版本的 YOLOv8 模型。但YOLOv5作为一个anchor base的目标检测的算法&#xff0c;YOLOv5可能比YOLOv8的效果更好。但是针对不同的数据…

CDN都有哪些优势?

内容分发网络是一个经策略性部署的整体系统&#xff0c;其中包含了分布式存储、负载均衡、网络请求的重定向和内容管理四个要求&#xff0c;CDN的主要核心则是内容管理和全局的网络流量管理&#xff0c;CDN可以确保内容会以一种非常高效的方式为用户的请求提供服务。 接下来就让…

Linux 第三十五章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

Kubernetes安装calico网络插件失败

今天啥也没干成&#xff0c;不想排版了。 接着昨天搭建k8s集群&#xff0c;安装calico插件&#xff0c;虚拟机一直卡。 # 在 master 节点上执行 # 下载 calico 配置文件&#xff0c;可能会网络超时 curl https://docs.tigera.io/archive/v3.25/manifests/calico.yaml -O # 修…

[svelte] 怎么引入fortawesome的icon样式

首先在项目的终端下执行以下命令 npm install fortawesome/fontawesome-free # 或者 yarn add fortawesome/fontawesome-free这样子可以把fontawsome的图标给下载到项目的对应文件中 一般都是在node_modules中 在fontawsome/fontawesome-free中就可以看到很多文件夹了 …

unapp写微信小程序封装水印相机组件怎么实现?

<template><view><!-- <cu-custom bgColor"bg-gradual-blue" :isBack"true"><block slot"backText">返回</block><block slot"content">编辑资料</block></cu-custom> --><…

Docker学习(10)搭建kubernetes集群

搭建kubernetes集群 1、官方部署方式&#xff1a; Minikube工具安装 Minikube是一种能够在计算机或者虚拟机(VM)内轻松运行单节点Kubernetes 集群的工具&#xff0c;可实现一键部署。这种方式安装的系统在企业中大多被当作测试系统使用。 使用yum安装 通过直接使用 epel-r…

三步在 vite 中配置 tailwindcss

前言 tailwindcss 是一个原子化的 css 工具&#xff0c;可以让你免于写 css&#xff0c;只写 html 即可原理&#xff1a;利用你写的 html 的 class 名称来生成 css 样式&#xff0c;理解为一个 postcss 插件或 loader 第一步&#xff1a;安装 tailwindcss npm i -D tailwind…

图片恢复的实用指南,为你拯救遗失的记忆!

随着科技的日新月异&#xff0c;我们的生活已被照片填满。它们记录着我们的喜怒哀乐&#xff0c;见证着每一个重要的时刻。但我们往往会因为各种原因将手机图片遗失&#xff0c;有什么方法可以恢复呢&#xff1f;本文将提供一份实用的图片恢复指南&#xff0c;帮助你找回那些遗…

山西大学化学化工学院朱凤祥教授简介

男&#xff0c;1989年出生&#xff0c;河南安阳人&#xff0c;2019年1月于山西大学化学化工学院任特聘教授&#xff0c;主要研究方向为有机催化&#xff0c;曾获国家自然科学基金资助&#xff08;2020-2023&#xff09;&#xff0c;迄今在国际高级杂志发表SCI论文20余篇。 200…

llama使用tutorial微调(windows版本)

Llama3-Tutorial/docs/assistant.md at main SmartFlowAI/Llama3-Tutorial GitHub 有一些命令需要修改 前期的安装还是要按照教程搞的 streamlit run ~/Llama3-Tutorial/tools/internstudio_web_demo.py \ ~/model/Meta-Llama-3-8B-Instruct 改为了 streamlit run .\Ll…