自定義@RequestBody注解如何獲取JSON數(shù)據(jù)
Spring MVC 的 @RequestBody 注解只能將請求體中的 JSON 數(shù)據(jù)封裝成 Bean,而無法對單一字段實施管理(例如 required、name 等屬性),這篇文章記錄了我的自定義注解 @JsonArg。
自定義注解
首先思考,我們需要這個注解做什么?
- 設(shè)置該字段的必選性(required)
- 設(shè)置該字段在請求體 JSON 中的 key 值(name、value)
- 設(shè)置該字段的默認(rèn)值(defaultValue)
于是我們的注解類 @JsonArg 至少應(yīng)該長這樣:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface JsonArg { ? ? @AliasFor("name") ? ? String value() default ""; ? ? @AliasFor("value") ? ? String name() default ""; ? ? boolean required() default true; ? ? String defaultValue() default ValueConstants.DEFAULT_NONE; }
其中:
@Retention(RetentionPolicy.RUNTIME)
聲明我們需要在運行期動態(tài)地獲取它地信息@Target(ElementType.PARAMETER)
聲明這個注解只能作用在方法參數(shù)
是的,它和 Spring MVC 自帶的 @RequestParam 注解非常像,但 @RequestParam 只能作用于 query parameters 和 form data。
自定義解析器
單純的定義注解類程序是無法知道我們要它做什么的,所以我們要告訴程序在遇到這個注解時該做什么。
public class JsonArgMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { ? ? /** ? ? ?* 表示關(guān)于 namedValue 的信息,包括名稱、是否需要它以及默認(rèn)值。 ? ? ?* ? ? ?* @param parameter 待處理的方法參數(shù) ? ? ?* @return {@link JsonArgNamedValueInfo} ? ? ?*/ ? ? @Override ? ? protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { ? ? ? ? JsonArg ann = parameter.getParameterAnnotation(JsonArg.class); ? ? ? ? return (ann != null ? new JsonArgNamedValueInfo(ann) : new JsonArgNamedValueInfo()); ? ? } ? ? /** ? ? ?* 解析方法 ? ? ?* ? ? ?* @param name ? ? ?待解析的 JSON 的 key ? ? ?* @param parameter 待處理的方法參數(shù) ? ? ?* @return 解析出來的值 ? ? ?*/ ? ? @Override ? ? protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { ? ? ? ? ContentCachingRequestWrapper contentCachingRequestWrapper = request.getNativeRequest(ContentCachingRequestWrapper.class); ? ? ? ? String body; ? ? ? ? assert contentCachingRequestWrapper != null; ? ? ? ? byte[] contentAsByteArray = contentCachingRequestWrapper.getContentAsByteArray(); ? ? ? ? if (contentAsByteArray.length == 0) { ? ? ? ? ? ? try (BufferedReader reader = contentCachingRequestWrapper.getReader()) { ? ? ? ? ? ? ? ? body = reader.lines().collect(Collectors.joining("")); ? ? ? ? ? ? } ? ? ? ? } else { ? ? ? ? ? ? body = new String(contentAsByteArray); ? ? ? ? } ? ? ? ? return JSONObject.parseObject(body).get(name); ? ? } ? ? /** ? ? ?* 是否支持該方法參數(shù) ? ? ?* ? ? ?* @param parameter 待處理的方法參數(shù) ? ? ?*/ ? ? @Override ? ? public boolean supportsParameter(MethodParameter parameter) { ? ? ? ? return true; ? ? } ? ? private static class JsonArgNamedValueInfo extends NamedValueInfo { ? ? ? ? public JsonArgNamedValueInfo() { ? ? ? ? ? ? super("", false, ValueConstants.DEFAULT_NONE); ? ? ? ? } ? ? ? ? public JsonArgNamedValueInfo(JsonArg annotation) { ? ? ? ? ? ? super(annotation.name(), annotation.required(), annotation.defaultValue()); ? ? ? ? } ? ? } }
注意到在 resolveName() 方法中我們獲取的 request 類型是 ContentCachingRequestWrapper。
這是因為默認(rèn)的 request 只能讀取一次請求體,而我們的解析器在解析每個方法參數(shù)時都需要讀取一次請求體。
包裝請求
添加過濾器將我們的請求轉(zhuǎn)換為所需要的 ContentCachingRequestWrapper。
/* ? ? 將request包裝成ContentCachingRequest,以反復(fù)讀取請求體 ?*/ @Component public class CachingRequestBodyFilter extends GenericFilterBean { ? ? @Override ? ? public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ? ? ? ? if (servletRequest instanceof HttpServletRequest) { ? ? ? ? ? ? filterChain.doFilter(new ContentCachingRequestWrapper((HttpServletRequest) servletRequest), servletResponse); ? ? ? ? } else { ? ? ? ? ? ? filterChain.doFilter(servletRequest, servletResponse); ? ? ? ? } ? ? } }
注冊解析器
最后,將自定義的解析器注冊到 Spring MVC。
@Configuration public class SpringMvcConfig implements WebMvcConfigurer { ? ? @Override ? ? public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { ? ? ? ? resolvers.add(new JsonArgMethodArgumentResolver()); ? ? } }
用法
和 @RequestParam 的用法相似。
public CommonResult<Object> signUp( ? ? ? ? ? ? @JsonArg(name = "uname") String username, ? ? ? ? ? ? @JsonArg(required = false, name = "pwd", defaultValue = "123") String password ? ? ) { ? ? ? ? log.info("[Username]: {}, [Password]: {}", username, password); ? ? ? ? return null; }
總結(jié)
大功告成!
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java使用html2image將html生成縮略圖圖片的實現(xiàn)示例
本文主要介紹了Java使用html2image將html生成縮略圖圖片的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12Java自帶定時任務(wù)ScheduledThreadPoolExecutor實現(xiàn)定時器和延時加載功能
今天小編就為大家分享一篇關(guān)于Java自帶定時任務(wù)ScheduledThreadPoolExecutor實現(xiàn)定時器和延時加載功能,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12java實現(xiàn)一個簡單TCPSocket聊天室功能分享
這篇文章主要為大家分享了java實現(xiàn)的一個簡單TCPSocket聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-04-04