基于@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<?> 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对象的属性名对应
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
上一篇: VR黑科技帮你在家看房源