热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于@RequestBody注解只能注入对象和map的解决

这篇文章主要介绍了@RequestBody注解只能注入对象和map的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完

@RequestBody注解只能注入对象和map的问题

前后端分离开发模式下,前后端数据交互全部采用json,所以在后端在采用spring框架的时候都会使用@ResponseBody(后端返回参数封装为json格式)和@RequestBody(前端请求携带json参数解析)注解。

但是在实际开发中,往往@RequestBody的使用会比较令人难受(超级难受),因为它spring官方只支持到将json解析为一个定义好的对象或者是一个通用性的map,而我们实际项目中经常传递的参数仅仅是一个或者是两个参数,这样的参数封装程对象总是有点大材小用的感觉,并且还消耗性能,而使用map又感觉操作比较繁琐,那这样的话可不可以使用简单的类似于@PathVariable(value="")这样的模式取请求数据呢????当然可以!!

1、自定义一个适应于这种情况的注解@RequestJson

package cn.annotation; 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; 
/**
 * Title:      RequestJson
 * @date       2018年9月10日
 * @version    V1.0
 * Description: 自定义请求json数据解析注解(主要解决单参数传递)
 */
@Target(ElementType.PARAMETER)//使用在参数上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
public @interface RequestJson {
    /**
     * 是否必须出现的参数
     */
    boolean required() default true;
 
    /**
     * 当value的值或者参数名不匹配时,是否允许解析最外层属性到该对象
     */
    boolean parseAllFields() default true;
 
    /**
     * 解析时用到的JSON的key
     */
    String value() default "";
}

2、自定义RequestJsonHandlerMethodArgumentResolver

实现HandlerMethodArgumentResolver(spring解析数据的接口)

package cn.config; 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; 
import cn.annotation.RequestJson; 
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
 
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
 
/**
 * Title:      RequestJsonHandlerMethodArgumentResolver
 * @date       2018年9月10日
 * @version    V1.0
 * Description: 自定义解析json数据
 */
public class RequestJsonHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
 private static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY";
  
    /**
     * 设置支持的方法参数类型
     * @param parameter 方法参数
     * @return 支持的类型
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 支持带@RequestJson注解的参数
        return parameter.hasParameterAnnotation(RequestJson.class);
    }
 
    /**
     * 参数解析,利用fastjson
     * 注意:非基本类型返回null会报空指针异常,要通过反射或者JSON工具类创建一个空对象
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
 
        String jsOnBody= getRequestBody(webRequest);
 
        JSONObject jsOnObject= JSON.parseObject(jsonBody);
        // 根据@RequestJson注解value作为json解析的key
        RequestJson parameterAnnotation = parameter.getParameterAnnotation(RequestJson.class);
        //注解的value是JSON的key
        String key = parameterAnnotation.value();
        Object value = null;
        // 如果@RequestJson注解没有设置value,则取参数名FrameworkServlet作为json解析的key
        if (StringUtils.isNotEmpty(key)) {
            value = jsonObject.get(key);
            // 如果设置了value但是解析不到,报错
            if (value == null && parameterAnnotation.required()) {
                throw new IllegalArgumentException(String.format("required param %s is not present", key));
            }
        } else {
            // 注解为设置value则用参数名当做json的key
            key = parameter.getParameterName();
            value = jsonObject.get(key);
        }
 
 
        Class<&#63;> parameterType = parameter.getParameterType();
        // 通过注解的value或者参数名解析,能拿到value进行解析
        if (value != null) {
            if (isBasicDataTypes(parameterType)) {
                return value;
            }
            return JSON.parseObject(value.toString(), parameterType);
        }
 
        // 解析不到则将整个json串解析为当前参数类型
        if (isBasicDataTypes(parameterType)) {
            if (parameterAnnotation.required()) {
                throw new IllegalArgumentException(String.format("required param %s is not present", key));
            } else {
                return null;
            }
        }
 
        Object result = parameterType.newInstance();
        // 非基本类型,不允许解析所有字段,返回null
        if (!parameterAnnotation.parseAllFields()) {
            // 如果是必传参数抛异常
            if (parameterAnnotation.required()) {
                throw new IllegalArgumentException(String.format("required param %s is not present", key));
            }
            // 否则返回空对象
            return result;
        }
       // 非基本类型,允许解析,将外层属性解析
        result = JSON.parseObject(jsonObject.toString(), parameterType);
        // 如果非必要参数直接返回,否则如果没有一个属性有值则报错
        if (!parameterAnnotation.required()) {
                return result;
        }else{
            boolean haveValue = false;
            Field[] declaredFields = parameterType.getDeclaredFields();
            for(Field field : declaredFields){
                field.setAccessible(true);
                if(field.get(result) != null){
                    haveValue = true;
                    break;
                }
            }
            if(!haveValue){
                throw new IllegalArgumentException(String.format("required param %s is not present", key));
            }
            return result;
        }
    }
 
    /**
     * 基本数据类型直接返回
     */
    @SuppressWarnings("rawtypes")
    private boolean isBasicDataTypes(Class clazz) {
  Set classSet = new HashSet<>();
        classSet.add(String.class);
        classSet.add(Integer.class);
        classSet.add(Long.class);
        classSet.add(Short.class);
        classSet.add(Float.class);
        classSet.add(Double.class);
        classSet.add(Boolean.class);
        classSet.add(Character.class);
        return classSet.contains(clazz);
    }
 
    /**
     * 获取请求体JSON字符串
     */
    private String getRequestBody(NativeWebRequest webRequest) {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
 
        // 有就直接获取
        String jsOnBody= (String) webRequest.getAttribute(JSONBODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
        // 没有就从请求中读取
        if (jsOnBody== null) {
            try {
                jsOnBody= IOUtils.toString(servletRequest.getReader());
                webRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return jsonBody;
 }
}

3、将上述配置应用到spring项目中

重写addArgumentResolvers方法

package cn.config; 
import java.nio.charset.Charset;
import java.util.List; 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
/**
 * Title:      WebConfig
 * @date       2018年9月10日
 * @version    V1.0
 * Description: 将自定义注解配置到spring
 */
 
@Configuration
@SuppressWarnings("deprecation")
public class WebConfig extends WebMvcConfigurerAdapter{
 @Override
    public void addArgumentResolvers(List argumentResolvers) {
        argumentResolvers.add(new RequestJsonHandlerMethodArgumentResolver());
    }
 
    @Bean
    public HttpMessageConverter responseBodyConverter() {
        return new StringHttpMessageConverter(Charset.forName("UTF-8"));
    }
 
    @Override
    public void configureMessageConverters(List> converters) {
        super.configureMessageConverters(converters);
        converters.add(responseBodyConverter());
    }
}

4、配置完成了,简单使用

单参数:(不加value默认为参数名)

或者:

多参数:

@RequestBody注解的使用问题

今天遇到的问题:@RequestBody的使用问题

先看一下@RequestBody的作用

在这里插入图片描述

我想获取json字符串某个字段值,看截图:

在这里插入图片描述

看一下控制台的输出信息:

在这里插入图片描述

what ?这什么情况,为什么拿到的是整个json字符串,然后我继续测试

在这里插入图片描述

给了一个400

在这里插入图片描述

what ?这又是什么情况 (好像只能有一个@RequestBody)我想参数如果是整形的话能不能获取,我继续进行测试代码:

在这里插入图片描述

传参:

在这里插入图片描述

又给了一个400 (好像只能是String类型) 测试引用类型对象

在这里插入图片描述

代码:

在这里插入图片描述

传参:

在这里插入图片描述

控制台打印:

在这里插入图片描述

测试成功。

个人总结:

1) 一个方法只能有一个@RequestBody

2) 如果接收参数是字符串类型的,获取的是整个json字符串

3) 如果接受的参数是引用对象,@requestBody User user 会将json字符串中的值赋予user中对应的属性上

需要注意的是,json字符串中key必须和User对象的属性名对应

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程笔记。


推荐阅读
  • Java如何导入和导出Excel文件的方法和步骤详解
    本文详细介绍了在SpringBoot中使用Java导入和导出Excel文件的方法和步骤,包括添加操作Excel的依赖、自定义注解等。文章还提供了示例代码,并将代码上传至GitHub供访问。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 本文讨论了如何使用Web.Config进行自定义配置节的配置转换。作者提到,他将msbuild设置为详细模式,但转换却忽略了带有替换转换的自定义部分的存在。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
author-avatar
故事与酒
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有