自定义@RequestBody注解如何获取JSON数据
Spring MVC 的 @RequestBody 注解只能将请求体中的 JSON 数据封装成 Bean,而无法对单一字段实施管理(例如 required、name 等属性),这篇文章记录了我的自定义注解 @JsonArg。
自定义注解
首先思考,我们需要这个注解做什么?
- 设置该字段的必选性(required)
- 设置该字段在请求体 JSON 中的 key 值(name、value)
- 设置该字段的默认值(defaultValue)
于是我们的注解类 @JsonArg 至少应该长这样:
@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)
声明我们需要在运行期动态地获取它地信息@Target(ElementType.PARAMETER)
声明这个注解只能作用在方法参数
是的,它和 Spring MVC 自带的 @RequestParam 注解非常像,但 @RequestParam 只能作用于 query parameters 和 form data。
自定义解析器
单纯的定义注解类程序是无法知道我们要它做什么的,所以我们要告诉程序在遇到这个注解时该做什么。
public class JsonArgMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { /** * 表示关于 namedValue 的信息,包括名称、是否需要它以及默认值。 * * @param parameter 待处理的方法参数 * @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 待处理的方法参数 * @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); } /** * 是否支持该方法参数 * * @param parameter 待处理的方法参数 */ @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。
这是因为默认的 request 只能读取一次请求体,而我们的解析器在解析每个方法参数时都需要读取一次请求体。
包装请求
添加过滤器将我们的请求转换为所需要的 ContentCachingRequestWrapper。
/* 将request包装成ContentCachingRequest,以反复读取请求体 */ @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; }
总结
大功告成!
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
Springboot整合Swagger2和Swagger3全过程
这篇文章主要介绍了Springboot整合Swagger2和Swagger3全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-07-07解决SpringBoot整合Mybatis扫描不到Mapper的问题
这篇文章主要介绍了解决SpringBoot整合Mybatis扫描不到Mapper的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-04-04Spring Boot与Kotlin 整合全文搜索引擎Elasticsearch的示例代码
本篇文章主要介绍了Spring Boot与Kotlin 整合全文搜索引擎Elasticsearch的示例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2018-01-01SpringBoot如何使用feign实现远程接口调用和错误熔断
这篇文章主要介绍了SpringBoot如何使用feign实现远程接口调用和错误熔断,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下2019-12-12SpringBoot在项目停止(服务停止/关闭退出)之后执行的方法
这篇文章主要给大家介绍了SpringBoot在项目停止(服务停止/关闭退出)之后执行的两种方法,实现DisposableBean接口和使用@PreDestroy注解,文中有详细的代码讲解,具有一定的参考价值,需要的朋友可以参考下2023-12-12Java通过MyBatis框架对MySQL数据进行增删查改的基本方法
MyBatis框架由Java的JDBC API进一步封装而来,在操作数据库方面效果拔群,接下来我们就一起来看看Java通过MyBatis框架对MySQL数据进行增删查改的基本方法:2016-06-06Netty分布式pipeline传播inbound事件源码分析
这篇文章主要为大家介绍了Netty分布式pipeline传播inbound事件的源码分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步2022-03-03
最新评论