最近项目中类似:
multiRequestBodyDemo(@MultiRequestBody("dog")
Dog dog, @MultiRequestBody("user") User user)
的注解看到了很多,那么这些注解是Spring MVC自带的吗?当然不是,spring MVC中自带的是@RequestBody的注解,这个注解有什么作用呢?这个注解可以将前端传进来的json数据进行解析成json数据。而如果我们没有采样@MultiRequestBody时,通常的做法是将其首先转成json首先转成json,然后进行json数据解析,然后对相关的属性进行逐一获取。下面的例子来源于网上,同时也是我们常用的解析方式之一。但是,如果我们获取属性过多,必然就会带来一个问题,对于代码会显得很长,不够优雅。
那还有一种方式那就是采样对象去接收,但是对象接收,但是如果是多个对象呢?那怎么解决这个问题?
此时就可以用到:
multiRequestBodyDemo(@MultiRequestBody("dog")
Dog dog, @MultiRequestBody("user") User user)
这种方式进行接收了。但spring boot是不支持这种方式的。因此,就需要自己写一个解析器来解析这样的传入方式和接收的方式。通常,比如我们有分页和对象时,就可以采用这种方式进行接收。
/*** 修改或者新增热门搜索* @param hotSearch* @param request* @return*/value="/servehotselectiveajax") (publicintservehotselectiveajax(HttpServletRequestreq,HttpServletResponseresp,JSONObjectobj) { intcount=0; intcountAddHotSearch=0; intcountEditHotSearch=0; LOGGER.info("data:"+obj.toJSONString()); //data:{"createArr":[{"hotSearchId":"","keyword":"ss","sort":"5","tempid":"21"}],"modifyArr":[{"hotSearchId":"205","keyword":"华为","sort":"2","tempid":"21"},{"hotSearchId":"206","keyword":"游戏本","sort":"3","tempid":"21"},{"hotSearchId":"207","keyword":"平板电视","sort":"3","tempid":"21"},{"hotSearchId":"208","keyword":"连衣裙","sort":"4","tempid":"21"}]}Stringdata=obj.toJSONString(); //解析json数据JSONObjectjson=JSON.parseObject(data); StringcreateArr=json.getString("createArr"); StringmodifyArr=json.getString("modifyArr"); if(StringUtils.isNotEmpty(createArr)){ JSONArraycreateArray=JSONArray.parseArray(createArr); for(inti=0;i<createArray.size();i++){ LongtempId=JSONObject.parseObject(JSONObject.toJSONString(createArray.get(i))).getLong("tempId"); Stringkeyword=JSONObject.parseObject(JSONObject.toJSONString(createArray.get(i))).getString("keyword"); Integersort=JSONObject.parseObject(JSONObject.toJSONString(createArray.get(i))).getInteger("sort"); //创建热门搜索对象HotSearchhotSearch=newHotSearch(); hotSearch.setTempid(tempId); hotSearch.setKeyword(keyword); hotSearch.setSort(sort); hotSearch.setCreateDate(newDate()); hotSearch.setDelFlag("0"); //添加热门搜索信息countAddHotSearch=hotSearchService.addHotSearchSelective(hotSearch); } } if(StringUtils.isNotEmpty(modifyArr)){ JSONArraymodifyArray=JSONArray.parseArray(modifyArr); for(inti=0;i<modifyArray.size();i++){ LonghotSearchId=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getLong("id"); LongtempId=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getLong("tempId"); Stringkeyword=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getString("keyword"); Integersort=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getInteger("sort"); //创建热门搜索对象HotSearchhotSearch=newHotSearch(); hotSearch.setHotSearchId(hotSearchId); hotSearch.setTempid(tempId); hotSearch.setKeyword(keyword); hotSearch.setSort(sort); //修改热门搜索信息countEditHotSearch=hotSearchService.modifyHostSearchSelectiveById(hotSearch); } } //判断修改或者新增成功if(countAddHotSearch>0||countEditHotSearch>0){ count=1; } returncount; }
MultiRequestBody解析器
解决的问题:
1、单个字符串等包装类型都要写一个对象才可以用接收;2、多个对象需要封装到一个对象里才可以用接收。主要优势:1、支持通过注解的value指定JSON的key来解析对象。2、支持通过注解无value,直接根据参数名来解析对象3、支持基本类型的注入4、支持GET和其他请求方式注入5、支持通过注解无value且参数名不匹配JSON串key时,根据属性解析对象。6、支持多余属性(不解析、不报错)、支持参数“共用”(不指定value时,参数名不为JSON串的key)7、支持当value和属性名找不到匹配的key时,对象是否匹配所有属性。
其思路是将前端传入的数据进行获取,也即jsonbody
, 获取请求体JSON字符串。获取之后,将其转成jsonObject。获取自定义元注解@MultiRequestBody中的value,如果@MultiRequestBody注解没有设置value,则取参数名FrameworkServlet作为json解析的key。默认是没有的,因此需要获取controller中的注解value值信息作为key,然后通过key拿到属性信息。进行相应的解析。下面是作者 Wangyang Liu的解析具体实现,具体代码可以到github上去获取,https://github.com/chujianyun/Spring-MultiRequestBody
/*** MultiRequestBody解析器* 解决的问题:* 1、单个字符串等包装类型都要写一个对象才可以用@RequestBody接收;* 2、多个对象需要封装到一个对象里才可以用@RequestBody接收。* 主要优势:* 1、支持通过注解的value指定JSON的key来解析对象。* 2、支持通过注解无value,直接根据参数名来解析对象* 3、支持基本类型的注入* 4、支持GET和其他请求方式注入* 5、支持通过注解无value且参数名不匹配JSON串key时,根据属性解析对象。* 6、支持多余属性(不解析、不报错)、支持参数“共用”(不指定value时,参数名不为JSON串的key)* 7、支持当value和属性名找不到匹配的key时,对象是否匹配所有属性。** @author Wangyang Liu QQ: 605283073* @date 2018/08/27*/publicclassMultiRequestBodyArgumentResolverimplementsHandlerMethodArgumentResolver { privatestaticfinalStringJSONBODY_ATTRIBUTE="JSON_REQUEST_BODY"; /*** 设置支持的方法参数类型** @param parameter 方法参数* @return 支持的类型*/publicbooleansupportsParameter(MethodParameterparameter) { // 支持带@MultiRequestBody注解的参数returnparameter.hasParameterAnnotation(MultiRequestBody.class); } /*** 参数解析,利用fastjson* 注意:非基本类型返回null会报空指针异常,要通过反射或者JSON工具类创建一个空对象*/publicObjectresolveArgument(MethodParameterparameter, ModelAndViewContainermavContainer, NativeWebRequestwebRequest, WebDataBinderFactorybinderFactory) throwsException { StringjsonBody=getRequestBody(webRequest); JSONObjectjsonObject=JSON.parseObject(jsonBody); // 根据@MultiRequestBody注解value作为json解析的keyMultiRequestBodyparameterAnnotation=parameter.getParameterAnnotation(MultiRequestBody.class); //注解的value是JSON的keyStringkey=parameterAnnotation.value(); Objectvalue; // 如果@MultiRequestBody注解没有设置value,则取参数名FrameworkServlet作为json解析的keyif (StringUtils.isNotEmpty(key)) { value=jsonObject.get(key); // 如果设置了value但是解析不到,报错if (value==null&¶meterAnnotation.required()) { thrownewIllegalArgumentException(String.format("required param %s is not present", key)); } } else { // 注解为设置value则用参数名当做json的keykey=parameter.getParameterName(); value=jsonObject.get(key); } // 获取的注解后的类型 LongClass<?>parameterType=parameter.getParameterType(); // 通过注解的value或者参数名解析,能拿到value进行解析if (value!=null) { //基本类型if (parameterType.isPrimitive()) { returnparsePrimitive(parameterType.getName(), value); } // 基本类型包装类if (isBasicDataTypes(parameterType)) { returnparseBasicTypeWrapper(parameterType, value); // 字符串类型 } elseif (parameterType==String.class) { returnvalue.toString(); } // 其他复杂对象returnJSON.parseObject(value.toString(), parameterType); } // 解析不到则将整个json串解析为当前参数类型if (isBasicDataTypes(parameterType)) { if (parameterAnnotation.required()) { thrownewIllegalArgumentException(String.format("required param %s is not present", key)); } else { returnnull; } } // 非基本类型,不允许解析所有字段,必备参数则报错,非必备参数则返回nullif (!parameterAnnotation.parseAllFields()) { // 如果是必传参数抛异常if (parameterAnnotation.required()) { thrownewIllegalArgumentException(String.format("required param %s is not present", key)); } // 否则返回nullreturnnull; } // 非基本类型,允许解析,将外层属性解析Objectresult; try { result=JSON.parseObject(jsonObject.toString(), parameterType); } catch (JSONExceptionjsonException) { // TODO:: 异常处理返回null是否合理?result=null; } // 如果非必要参数直接返回,否则如果没有一个属性有值则报错if (!parameterAnnotation.required()) { returnresult; } else { booleanhaveValue=false; Field[] declaredFields=parameterType.getDeclaredFields(); for (Fieldfield : declaredFields) { field.setAccessible(true); if (field.get(result) !=null) { haveValue=true; break; } } if (!haveValue) { thrownewIllegalArgumentException(String.format("required param %s is not present", key)); } returnresult; } } /*** 基本类型解析*/privateObjectparsePrimitive(StringparameterTypeName, Objectvalue) { finalStringbooleanTypeName="boolean"; if (booleanTypeName.equals(parameterTypeName)) { returnBoolean.valueOf(value.toString()); } finalStringintTypeName="int"; if (intTypeName.equals(parameterTypeName)) { returnInteger.valueOf(value.toString()); } finalStringcharTypeName="char"; if (charTypeName.equals(parameterTypeName)) { returnvalue.toString().charAt(0); } finalStringshortTypeName="short"; if (shortTypeName.equals(parameterTypeName)) { returnShort.valueOf(value.toString()); } finalStringlongTypeName="long"; if (longTypeName.equals(parameterTypeName)) { returnLong.valueOf(value.toString()); } finalStringfloatTypeName="float"; if (floatTypeName.equals(parameterTypeName)) { returnFloat.valueOf(value.toString()); } finalStringdoubleTypeName="double"; if (doubleTypeName.equals(parameterTypeName)) { returnDouble.valueOf(value.toString()); } finalStringbyteTypeName="byte"; if (byteTypeName.equals(parameterTypeName)) { returnByte.valueOf(value.toString()); } returnnull; } /*** 基本类型包装类解析*/privateObjectparseBasicTypeWrapper(Class<?>parameterType, Objectvalue) { if (Number.class.isAssignableFrom(parameterType)) { Numbernumber= (Number) value; if (parameterType==Integer.class) { returnnumber.intValue(); } elseif (parameterType==Short.class) { returnnumber.shortValue(); } elseif (parameterType==Long.class) { returnnumber.longValue(); } elseif (parameterType==Float.class) { returnnumber.floatValue(); } elseif (parameterType==Double.class) { returnnumber.doubleValue(); } elseif (parameterType==Byte.class) { returnnumber.byteValue(); } } elseif (parameterType==Boolean.class) { returnvalue.toString(); } elseif (parameterType==Character.class) { returnvalue.toString().charAt(0); } returnnull; } /*** 判断是否为基本数据类型包装类*/privatebooleanisBasicDataTypes(Classclazz) { Set<Class>classSet=newHashSet<>(); 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(Byte.class); classSet.add(Character.class); returnclassSet.contains(clazz); } /*** 获取请求体JSON字符串*/privateStringgetRequestBody(NativeWebRequestwebRequest) { HttpServletRequestservletRequest=webRequest.getNativeRequest(HttpServletRequest.class); // 有就直接获取StringjsonBody= (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 (IOExceptione) { thrownewRuntimeException(e); } } returnjsonBody; } }
当然可以:
还可以:
还可以:
从测试的结果来看,都可以很方便的转成对应的对象信息,方便使用!