欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-19 15:08:12
目录@requestbody注解只能注入对象和map的问题1、自定义一个适应于这种情况的注解@requestjson2、自定义requestjsonhandlermethodargumentresol...

@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注解只能注入对象和map的解决

或者:

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

多参数:

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

@requestbody注解的使用问题

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

先看一下@requestbody的作用

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

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

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

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

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

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

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

给了一个400

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

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

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

传参:

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

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

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

代码:

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

传参:

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

控制台打印:

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

测试成功。

个人总结:

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

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

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

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

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