基于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.pswp.cn/web/13327.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13327.shtml
英文地址,請注明出處:http://en.pswp.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…