ingmvc对同名参数处理

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

sPRingmvc对同名参数处理扯淡:

中断发博客几个月,其实蛮不爽的,可能最近太忙太劳累了些,很多总结也没时间写,今天刚好遇到个小问题,就阅读下源码找找乐。因为考虑到网上大多是提供解决问题的方案,没有实际去看spring源码流程,所以就发个博文记录下,万一以后有同学搜到我的文章能深入看些东西吧。

问题描述:前端有多个相同name的input:

<inputtype="text"min="1"max="31"name="xName"value="1"/><inputtype="text"min="1"max="31"name="xName"value="2"/><inputtype="text"min="1"max="31"name="xName"value="3"/>

提交时流程原理:

一般容器在HTTP协议的Request传递到ServletContainer中后,Container就会为该次请求生成一个HTTPServletRequest对象,在HTTPServletRequest对象中,参数和值,放入到了Map中。

tomcat源码中放入map时的代码:

/*** Put name and value pair in map. When name already exist, add value

* to array of values.

*

*@parammap The map to populate

*@paramname The parameter name

*@paramvalue The parameter value*/privatestaticvoidputMapEntry( Map map, String name, String value) {

String[] newValues=null;

String[] oldValues=(String[]) map.get(name);if(oldValues ==null) {

newValues=newString[1];

newValues[0] =value;

}else{

newValues=newString[oldValues.length + 1];

System.arraycopy(oldValues,0, newValues, 0, oldValues.length);

newValues[oldValues.length]=value;

}

map.put(name, newValues);

}

可见同名的name,都会被放入数组的value中,而在servlet中,获取value的两个方法:

request.getParameter

request.getParameterValues

第一个获取的是数组的第一个值,第二个则获得整个数组。

springmvc框架在遇到这种同名参数提交,而参数中有逗号时,会出现问题:

比如我提交的值是 xname=123 和 xname=45,67

那么在进入action拿到参数时会变成string[] xname = ["123","45","67"]

就会影响业务逻辑正确执行。

解决方案:就是在提交后台前,将参数进行转码,后台再进行解码就可以了。至于采用哪一种转码解码方式,其实没有什么要求,只需要注意采用方案会将逗号转码即可,知道这个关键点就好了。

实践方案:

前端代码:

$("input[name='ssidName']").each(function(){

$(this).val(encodeURIComponent($(this).val()));

})

后端java代码:

URLDecoder.decode(ssidNames [i], "UTF-8" )

灵活点的想法是:采用可逆转的转码解码也可,比如base64。有兴趣的可以尝试。

源码阅读:

虽然知道spring有这个自动组装的功能,那么我就找到这段代码:

关于springmvc原理分析这文章不错:http://www.cnblogs.com/heavenyes/p/3905844.html

我就在它后面再更进一步分析同名参数是如何处理的。

完成request中的参数和方法参数上数据的绑定:

privateObject[] resolveHandlerArguments(Method handlerMethod, Object handler,

NativeWebRequest webRequest, ExtendedModelMap implicitModel)throwsException {//1.获取方法参数类型的数组Class[] paramTypes =handlerMethod.getParameterTypes();//声明数组,存参数的值Object[] args =newObject[paramTypes.length];//2.遍历参数数组,获取每个参数的值for(inti = 0; i < args.length; i++) {

MethodParameter methodParam=newMethodParameter(handlerMethod, i);

methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);

GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());

String paramName=null;

String headerName=null;booleanrequestBodyFound =false;

String cookieName=null;

String pathVarName=null;

String attrName=null;booleanrequired =false;

String defaultValue=null;booleanvalidate =false;intannotationsFound = 0;

Annotation[] paramAnns=methodParam.getParameterAnnotations();//处理参数上的注解for(Annotation paramAnn : paramAnns) {if(RequestParam.class.isInstance(paramAnn)) {

RequestParam requestParam=(RequestParam) paramAnn;

paramName=requestParam.value();

required=requestParam.required();

defaultValue=parseDefaultValueAttribute(requestParam.defaultValue());

annotationsFound++;

}elseif(RequestHeader.class.isInstance(paramAnn)) {

RequestHeader requestHeader=(RequestHeader) paramAnn;

headerName=requestHeader.value();

required=requestHeader.required();

defaultValue=parseDefaultValueAttribute(requestHeader.defaultValue());

annotationsFound++;

}elseif(RequestBody.class.isInstance(paramAnn)) {

requestBodyFound=true;

annotationsFound++;

}elseif(CookieValue.class.isInstance(paramAnn)) {

CookieValue cookieValue=(CookieValue) paramAnn;

cookieName=cookieValue.value();

required=cookieValue.required();

defaultValue=parseDefaultValueAttribute(cookieValue.defaultValue());

annotationsFound++;

}elseif(PathVariable.class.isInstance(paramAnn)) {

PathVariable pathVar=(PathVariable) paramAnn;

pathVarName=pathVar.value();

annotationsFound++;

}elseif(ModelAttribute.class.isInstance(paramAnn)) {

ModelAttribute attr=(ModelAttribute) paramAnn;

attrName=attr.value();

annotationsFound++;

}elseif(Value.class.isInstance(paramAnn)) {

defaultValue=((Value) paramAnn).value();

}elseif("Valid".equals(paramAnn.annotationType().getSimpleName())) {

validate=true;

}

}if(annotationsFound > 1) {thrownewIllegalStateException("Handler parameter annotations are exclusive choices - " +

"do not specify more than one such annotation on the same parameter: " +handlerMethod);

}if(annotationsFound == 0) {//如果没有注解Object argValue =resolveCommonArgument(methodParam, webRequest);if(argValue !=WebArgumentResolver.UNRESOLVED) {

args[i]=argValue;

}elseif(defaultValue !=null) {

args[i]=resolveDefaultValue(defaultValue);

}else{

Class paramType=methodParam.getParameterType();//将方法声明中的Map和Model参数,放到request中,用于将数据放到request中带回页面if(Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {

args[i]=implicitModel;

}elseif(sessionStatus.class.isAssignableFrom(paramType)) {

args[i]=this.sessionStatus;

}elseif(HttpEntity.class.isAssignableFrom(paramType)) {

args[i]=resolveHttpEntityRequest(methodParam, webRequest);

}elseif(Errors.class.isAssignableFrom(paramType)) {thrownewIllegalStateException("Errors/BindingResult argument declared " +

"without preceding model attribute. Check your handler method signature!");

}elseif(BeanUtils.isSimpleProperty(paramType)) {

paramName= "";

}else{

attrName= "";

}

}

}//从request中取值,并进行赋值操作if(paramName !=null) {//根据paramName从request中取值,如果没有通过RequestParam注解指定paramName,则使用asm读取class文件来获取paramNameargs[i] =resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);

}elseif(headerName !=null) {

args[i]=resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);

}elseif(requestBodyFound) {

args[i]=resolveRequestBody(methodParam, webRequest, handler);

}elseif(cookieName !=null) {

args[i]=resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);

}elseif(pathVarName !=null) {

args[i]=resolvePathVariable(pathVarName, methodParam, webRequest, handler);

}elseif(attrName !=null) {

WebDataBinder binder=resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);booleanassignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));if(binder.getTarget() !=null) {

doBind(binder, webRequest, validate,!assignBindingResult);

}

args[i]=binder.getTarget();if(assignBindingResult) {

args[i+ 1] =binder.getBindingResult();

i++;

}

implicitModel.putAll(binder.getBindingResult().getModel());

}

}//返回参数值数组returnargs;

}

resolveRequestParam方法将参数取出:

privateObject resolveRequestParam (String paramName,booleanrequired, String defaultValue,

MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)throwsException {

Class<?> paramType =methodParam.getParameterType();if(Map.class.isAssignableFrom(paramType) && paramName.length() == 0) {returnresolveRequestParamMap((Class<?extendsMap>) paramType, webRequest);

}if(paramName.length() == 0) {

paramName=getRequiredParameterName(methodParam);

}

Object paramValue=null;

MultipartRequest multipartRequest= webRequest.getNativeRequest(MultipartRequest.class);if(multipartRequest !=null) {

List<MultipartFile> files =multipartRequest.getFiles(paramName);if(!files.isEmpty()) {

paramValue= (files.size() == 1 ? files.get(0) : files);

}

}if(paramValue ==null) {//数组参数执行这行代码,相当于执行了request.getParameterValues(name)String[] paramValues =webRequest.getParameterValues(paramName);if(paramValues !=null) {//取出之后,如果是同名参数则赋值整个数组 paramValue此时是objectparamValue = (paramValues. length == 1 ? paramValues[0] : paramValues);

}

}if(paramValue ==null) {if(defaultValue !=null) {

paramValue=resolveDefaultValue(defaultValue);

}elseif(required) {

raiseMissingParameterException(paramName, paramType);

}

paramValue=checkValue(paramName, paramValue, paramType);

}

WebDataBinder binder= createBinder(webRequest,null, paramName);

initBinder(handlerForInitBinderCall, paramName, binder, webRequest);//将paramValue根据参数类型进行一次转换操作returnbinder.convertIfNecessary(paramValue, paramType, methodParam);

}

最终会执行到TypeConverterDelegate的convertIfNecessary方法:

public<T>T convertIfNecessary(String propertyName, Object oldValue, Object newValue,

Class<T> requiredType, TypeDescriptor typeDescriptor)throwsIllegalArgumentException {

Object convertedValue=newValue;//Custom editor for this type?PropertyEditor editor =this. propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

ConversionFailedException firstAttemptEx=null;//No custom editor but custom ConversionService specified?ConversionService conversionService =this. propertyEditorRegistry.getConversionService();if(editor ==null&& conversionService !=null&& convertedValue !=null&& typeDescriptor !=null) {

TypeDescriptor sourceTypeDesc=TypeDescriptor.forObject(newValue);

TypeDescriptor targetTypeDesc=typeDescriptor;if(conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {try{return(T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);

}catch(ConversionFailedException ex) {//fallback to default conversion logic belowfirstAttemptEx =ex;

}

}

}//Value not of required type?if(editor !=null|| (requiredType !=null&& !ClassUtils.isAssignableValue(requiredType, convertedValue))) {if(requiredType !=null&& Collection.class.isAssignableFrom(requiredType) && convertedValueinstanceofString) {

TypeDescriptor elementType=typeDescriptor.getElementTypeDescriptor();if(elementType !=null&& Enum.class.isAssignableFrom(elementType.getType())) {

convertedValue=StringUtils.commaDelimitedListToStringArray((String) convertedValue);

}

}if(editor ==null) {

editor=findDefaultEditor(requiredType);

}

convertedValue=doConvertValue(oldValue, convertedValue, requiredType, editor);

}booleanstandardConversion =false;if(requiredType !=null) {//Try to apply some standard type conversion rules if appropriate.if(convertedValue !=null) {//因为我们的type是数组,所以执行这段逻辑,他会执行StringUtils.commaDelimitedListToStringArrayif(requiredType.isArray()) {//Array required -> apply appropriate conversion of elements.if(convertedValueinstanceofString && Enum.class.isAssignableFrom(requiredType.getComponentType())) {

convertedValue=StringUtils.commaDelimitedListToStringArray((String) convertedValue);

}return(T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());

}elseif(convertedValueinstanceofCollection) {//Convert elements to target type, if determined.convertedValue =convertToTypedCollection(

(Collection) convertedValue, propertyName, requiredType, typeDescriptor);

standardConversion=true;

}elseif(convertedValueinstanceofMap) {//Convert keys and values to respective target type, if determined.convertedValue =convertToTypedMap(

(Map) convertedValue, propertyName, requiredType, typeDescriptor);

standardConversion=true;

}if(convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {

convertedValue= Array.get(convertedValue, 0);

standardConversion=true;

}if(String.class.equals(requiredType) &&ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {//We can stringify any primitive value...return(T) convertedValue.toString();

}elseif(convertedValueinstanceofString && !requiredType.isInstance(convertedValue)) {if(firstAttemptEx ==null&& !requiredType.isInterface() && !requiredType.isEnum()) {try{

Constructor strCtor= requiredType.getConstructor(String.class);return(T) BeanUtils.instantiateClass(strCtor, convertedValue);

}catch(NoSuchMethodException ex) {//proceed with field lookupif(logger.isTraceEnabled()) {

logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);

}

}catch(Exception ex) {if(logger.isDebugEnabled()) {

logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);

}

}

}

String trimmedValue=((String) convertedValue).trim();if(requiredType.isEnum() && "".equals(trimmedValue)) {//It's an empty enum identifier: reset the enum value to null.returnnull;

}

convertedValue=attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);

standardConversion=true;

}

}if(!ClassUtils. isAssignableValue(requiredType, convertedValue)) {if(firstAttemptEx !=null) {throwfirstAttemptEx;

}//Definitely doesn't match: throw IllegalArgumentException/IllegalStateExceptionStringBuilder msg =newStringBuilder();

msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType (newValue));

msg.append("] to required type [").append(ClassUtils.getQualifiedName (requiredType)).append("]");if(propertyName !=null) {

msg.append(" for property '").append(propertyName).append("'");

}if(editor !=null) {

msg.append(": PropertyEditor [").append(editor.getClass().getName()).append("] returned inappropriate value of type [").append(

ClassUtils. getDescriptiveType(convertedValue)).append("]");thrownewIllegalArgumentException(msg.toString());

}else{

msg.append(": no matching editors or conversion strategy found");thrownewIllegalStateException(msg.toString());

}

}

}if(firstAttemptEx !=null) {if(editor ==null&& !standardConversion && requiredType !=null&& !Object.class.equals(requiredType)) {throwfirstAttemptEx;

}

logger.debug("Original ConversionService attempt failed - ignored since " +

"PropertyEditor based conversion eventually succeeded", firstAttemptEx);

}return(T) convertedValue;

}

StringUtils.commaDelimitedListToStringArray代码:

publicstaticString[] commaDelimitedListToStringArray (String str) {returndelimitedListToStringArray(str, ",");

}

最终执行代码,也就是哪里把这个逗号的参数区分成两个参数的地方,众里寻他千百度啊:

publicstaticString[] delimitedListToStringArray (String str, String delimiter, String charsToDelete) {if(str ==null) {returnnewString[0];

}if(delimiter ==null) {returnnewString[] {str};

}

List<String> result =newArrayList<String>();if( "".equals(delimiter)) {for(inti = 0; i < str.length(); i++) {

result.add( deleteAny(str.substring(i, i+ 1), charsToDelete));

}

}else{intpos = 0;intdelPos;//逗号分隔,组装新的listwhile((delPos = str.indexOf(delimiter, pos)) != -1) {

result.add( deleteAny(str.substring(pos, delPos), charsToDelete));

pos= delPos +delimiter.length();

}if(str.length() > 0 && pos <=str.length()) {//Add rest of String, but not in case of empty input.result.add( deleteAny(str.substring(pos), charsToDelete));

}

}returntoStringArray(result);

}

让我们继续前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不会成功。

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航