自定义@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;
}总结
大功告成!
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
Java使用Collections工具类对List集合进行排序
这篇文章主要介绍了Java使用Collections工具类对List集合进行排序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下2019-10-10
解析ConcurrentHashMap: put方法源码分析
ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment的结构和HashMap类似,是一种数组和链表结构,今天给大家普及java面试常见问题---ConcurrentHashMap知识,一起看看吧2021-06-06
SpringBoot整合redis中的JSON序列化文件夹操作小结
在我们日常的项目开发中,使用redis作为缓存,来提高系统访问速度和缓解系统压力,在使用中遇到几个问题,本文给大家详细总结下,对SpringBoot整合redis JSON序列化相关知识感兴趣的朋友一起看看吧2022-02-02
spring scheduled单线程和多线程使用过程中的大坑
本文主要介绍了spring scheduled单线程和多线程使用过程中的大坑,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2022-01-01


最新评论