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

目录
  • @RequestBody注解只能注入对象和map的问题
    • 1、自定义一个适应于这种情况的注解@RequestJson
    • 2、自定义RequestJsonHandlerMethodArgumentResolver
    • 3、将上述配置应用到spring项目中
    • 4、配置完成了,简单使用
  • @RequestBody注解的使用问题
    • 先看一下@RequestBody的作用
    • 个人总结:

@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<?> 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<Class> 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<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new RequestJsonHandlerMethodArgumentResolver());
    }

    @Bean
    public HttpMessageConverter<String> responseBodyConverter() {
        return new StringHttpMessageConverter(Charset.forName("UTF-8"));
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> 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对象的属性名对应

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 解决SpringMVC使用@RequestBody注解报400错误的问题

    一般使用@RequestBody接收的时候报400都是传入的json字符串和对应封装的对象不对应造成的 首先要注意 封装的对象中的字段类型有没有Date类型或者int等类型的,如果有的话,在set方法中统一将传入的参数要调整为String类型 在set方法中进行参数的转换 比如有一个Date类型的字段 private Date startTime; public Date getStartTime() { return startTime; } public void setStartTime(

  • @ResponseBody 和 @RequestBody 注解的区别

    @ResponseBody 和 @RequestBody 注解的区别 1 前言 在详述 @ResponseBody 和 @RequestBody 注解之前,咱先了解一下 @RequestMapping 注解,@RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径:用于方法上,表示在类的父路径下追加方法上注解中的地址将会访问到该方法.例如. /** * 用于类上,可以没有 */ @RequestMapping

  • 解读@RequestBody的正确使用方法

    本文主要研究的是关于@RequestBody的正确使用方法的相关内容,具体如下. 最近在接收一个要离职同事的工作,接手的项目是用SpringBoot搭建的,其中看到了这样的写法: @RequestMapping("doThis") public String doThis(HttpServletRequest request, @RequestParam("id") Long id, // 用户ID @RequestParam("back_url"

  • @RequestBody的使用详解

    提示:建议一定要看后面的@RequestBody的核心逻辑源码以及六个重要结论!本文前半部分的内容都是一些基 本知识常识,可选择性跳过. 声明:本文是基于SpringBoot,进行的演示说明. 基础知识介绍: @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的):GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交.在后端的同一个接收方法里,@RequestBody与@Request

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

    目录 @RequestBody注解只能注入对象和map的问题 1.自定义一个适应于这种情况的注解@RequestJson 2.自定义RequestJsonHandlerMethodArgumentResolver 3.将上述配置应用到spring项目中 4.配置完成了,简单使用 @RequestBody注解的使用问题 先看一下@RequestBody的作用 个人总结: @RequestBody注解只能注入对象和map的问题 前后端分离开发模式下,前后端数据交互全部采用json,所以在后端在采用s

  • Spring IOC容器的Bean管理基于注解属性注入方式

    目录 基于注解方式进行属性注入 一.@Autowired 1. 添加对象注解 2. 在service中注入dao对象 3. 创建测试函数测试效果 二.@Qualifier 三.@Resource 1. 替代 @Autowired 2. 替换 @Qualifier 四.@Value 基于注解方式进行属性注入 涉及到 4 个注解 @Autowired:根据属性类型,进行自动装配 @Qualifier:根据属性名称进行注入,跟 @Autowired 一起使用 @Resource:既可以根据类型注入,也

  • 基于@PostConstruct注解的使用,解决向静态变量注入值

    目录 @PostConstruct注解的使用,向静态变量注入值 说说思路 @PostConstruct和静态变量注入和spring初始化 执行顺序 关于spring初始化操作 @PostConstruct注解的使用,向静态变量注入值 今天在编写工具类时遇到了一个问题,一般在定义工具类方时,我们会将工具类中的方法定义成static类型,使用时可以通过类名.方法名获取该方法,无需实例化出对象才能使用其内部方法,但是当有些参数在配置文件中定义时,我们需要拿到这些参数就需要在工具类方法使用@Value注

  • Spring AOP如何在注解上使用SPEL表达式注入对象

    目录 在注解上使用SPEL表达式注入对象 场景描述 具体案例 补充 Spring属性注入方式之SPEL表达式 在注解上使用SPEL表达式注入对象 场景描述 在平时开发中,我们经常通过定义一些注解,进行轻量级开发. 今天主要研究的内容是关于如何在注解上通过spel表达式注入对象,以此调用注入对象的具体业务处理逻辑,然后在通过对表达式的解析,进而获取该业务逻辑处理的结果,类似于Spring Security中的@PreAuthorize, @PreAuthorize, @PostAuthorize等

  • 基于Vue3+TypeScript的全局对象的注入和使用详解

    目录 1.Vue2的全局挂载 2.Vue3+TypeScript的全局挂载 3.Vue3+TypeScript的全局挂载的对象接口定义 刚完成一些前端项目的开发,腾出精力来总结一些前端开发的技术点,以及继续完善基于SqlSugar的开发框架循序渐进介绍的系列文章,本篇随笔主要介绍一下基于Vue3+TypeScript的全局对象的注入和使用.我们知道在Vue2中全局注入一个全局变量使用protoType的方式,很方便的就注入了,而Vue3则不能通过这种方式直接使用,而是显得复杂一些,不过全局变量的

  • 亲测SpringBoot参数传递及@RequestBody注解---踩过的坑及解决

    目录 SpringBoot 参数传递及@RequestBody注解注意点 前台正确的js书写格式是 后台正确的Controller书写格式是 RequestBody 作为参数使用 为了证实这个想法,自己书写一个请求类 小结一下 SpringBoot 参数传递及@RequestBody注解注意点 前台正确的js书写格式是 //点击查询, 执行下面这个函数 $("#searchByCriteria").click(function () { //var paramdata = $(&quo

  • spring mvc中@RequestBody注解的作用说明

    目录 @RequestBody的作用 实现案例 1. jsp页面 2. controller 3. spring-mvc 配置文件 4. 页面访问 @RequestBody原理 1. 执行流程 2. 注册流程 3. 设计优点 附:常见的MVC参数注解 @RequestBody的作用 @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的),所以只能发送POST请求. GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交

  • 基于@ComponentScan注解的使用详解

    目录 @ComponentScan注解的使用 一.注解定义 二.使用 1.环境准备 2.excludeFilters的使用 3.includeFilters的使用 4.自定义过滤规则 关于@ComponentScan注解的一些细节 @ComponentScan注解的使用 一.注解定义 @ComponentScan注解的定义如下: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatabl

  • Java SpringBoot容器注入对象详解

    目录 1.注入的方式 方式一:使用Import注解 方式二:使用@Service 或者@Component等注解注入到容器中 方式三:使用@Configuration和@Bean组合实现 2.注入是增加条件判断注解 3.构造方法时带参数注入 方式1:使用spring xml实现 方式2:使用@Autowired 方式3使用@Configuration和@Bean组合 4.对象注入时的一些总结 总结 1.注入的方式 方式一:使用Import注解 增加一个类HelloCompent package

随机推荐