SpringBoot Controller 中使用多個@RequestBody


曾幾何時,開發了一個小小工具,然后想一次性提交多個表單,如:

{
    "company":{
        "id":"4308D4EDFA8B8D0505CDDD4A9EADA818",
        "name":"11",
        "address":"112"
    },
    "computer":[
        {
            "mainEngine":"a111",
            "displayDevice":"b2222",
            "printer":"c33333",
            "ukey":"d44444",
            "network":"1",
            "id":"201BE45CA745976EF5F5627AFC41DFA0",
            "companyId":"4308D4EDFA8B8D0505CDDD4A9EADA818"
        }
    ],
    "liaison":[
        {
            "name":"1",
            "cellPhoneNumber":"1111111111",
            "id":"FFF27320B5042A0E41686150D7C34DD6",
            "companyId":"4308D4EDFA8B8D0505CDDD4A9EADA818"
        }
    ]
}

結果發現spring boot 的controller的@RequestBody不提供多個對象的映射的,故想方設法搞定啊!

一、新建一個@interface的MultiRequestBody:

package cn.com.css.target;

//2、編寫解析的方法注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Controller中方法接收多個JSON對象
 * @date 2018/08/27
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiRequestBody {
    /**
     * 是否必須出現的參數
     */
    boolean required() default true;

    /**
     * 當value的值或者參數名不匹配時,是否允許解析最外層屬性到該對象
     */
    boolean parseAllFields() default true;

    /**
     * 解析時用到的JSON的key
     */
    String value() default "";
}

二、新建MultiRequestBodyArgumentResolver類

package cn.com.css.utils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

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;

//1、重寫方法參數解析器

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

import cn.com.css.target.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時,對象是否匹配所有屬性。
 *
 * @date 2018/08/27
 */
public class MultiRequestBodyArgumentResolver implements HandlerMethodArgumentResolver {

	private static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY";

	/**
	 * 設置支持的方法參數類型
	 *
	 * @param parameter 方法參數
	 * @return 支持的類型
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		// 支持帶@MultiRequestBody注解的參數
		return parameter.hasParameterAnnotation(MultiRequestBody.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);
		// 根據@MultiRequestBody注解value作為json解析的key
		MultiRequestBody parameterAnnotation = parameter.getParameterAnnotation(MultiRequestBody.class);
		// 注解的value是JSON的key
		String key = parameterAnnotation.value();
		Object value;
		// 如果@MultiRequestBody注解沒有設置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);
		}

		// 獲取的注解后的類型 Long
		Class<?> parameterType = parameter.getParameterType();
		// 通過注解的value或者參數名解析,能拿到value進行解析
		if (value != null) {
			// 基本類型
			if (parameterType.isPrimitive()) {
				return parsePrimitive(parameterType.getName(), value);
			}
			// 基本類型包裝類
			if (isBasicDataTypes(parameterType)) {
				return parseBasicTypeWrapper(parameterType, value);
				// 字符串類型
			} else if (parameterType == String.class) {
				return value.toString();
			} else if (parameterType == List.class) {// 是不是list對象
				return parseListTypeWrapper(parameterType, value, parameter);

			}
			// 其他復雜對象
			System.out.println(parameterType);
			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;
			}
		}

		// 非基本類型,不允許解析所有字段,必備參數則報錯,非必備參數則返回null
		if (!parameterAnnotation.parseAllFields()) {
			// 如果是必傳參數拋異常
			if (parameterAnnotation.required()) {
				throw new IllegalArgumentException(String.format("required param %s is not present", key));
			}
			// 否則返回null
			return null;
		}
		// 非基本類型,允許解析,將外層屬性解析
		Object result;
		try {
			result = JSON.parseObject(jsonObject.toString(), parameterType);
		} catch (JSONException jsonException) {
			// TODO:: 異常處理返回null是否合理?
			result = null;
		}

		// 如果非必要參數直接返回,否則如果沒有一個屬性有值則報錯
		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;
		}
	}

	*/**
	 * 方法名: parseListTypeWrapper<br/>
	 * 描述: List類型時轉成對應的實列對象<br/>
	 * 參數: @param parameterType
	 * 參數: @param value
	 * 參數: @param parameter
	 * 參數: @return <br/> 
	 * 返回類型: Object <br/>    
	 * 異常:
	 */
	private Object parseListTypeWrapper(Class<?> parameterType, Object value, MethodParameter parameter) {
		Type type = parameter.getGenericParameterType();
		if (type instanceof ParameterizedType) {
			ParameterizedType pt = (ParameterizedType) type;
			for (Type arg : pt.getActualTypeArguments()) {
				parameterType = (Class<?>) arg;
			}
		}
		return JSONObject.parseArray(value.toString(), parameterType);
	}*

	/**
	 * 基本類型解析
	 */
	private Object parsePrimitive(String parameterTypeName, Object value) {
		final String booleanTypeName = "boolean";
		if (booleanTypeName.equals(parameterTypeName)) {
			return Boolean.valueOf(value.toString());
		}
		final String intTypeName = "int";
		if (intTypeName.equals(parameterTypeName)) {
			return Integer.valueOf(value.toString());
		}
		final String charTypeName = "char";
		if (charTypeName.equals(parameterTypeName)) {
			return value.toString().charAt(0);
		}
		final String shortTypeName = "short";
		if (shortTypeName.equals(parameterTypeName)) {
			return Short.valueOf(value.toString());
		}
		final String longTypeName = "long";
		if (longTypeName.equals(parameterTypeName)) {
			return Long.valueOf(value.toString());
		}
		final String floatTypeName = "float";
		if (floatTypeName.equals(parameterTypeName)) {
			return Float.valueOf(value.toString());
		}
		final String doubleTypeName = "double";
		if (doubleTypeName.equals(parameterTypeName)) {
			return Double.valueOf(value.toString());
		}
		final String byteTypeName = "byte";
		if (byteTypeName.equals(parameterTypeName)) {
			return Byte.valueOf(value.toString());
		}
		return null;
	}

	/**
	 * 基本類型包裝類解析
	 */
	private Object parseBasicTypeWrapper(Class<?> parameterType, Object value) {
		if (Number.class.isAssignableFrom(parameterType)) {
			Number number = (Number) value;
			if (parameterType == Integer.class) {
				return number.intValue();
			} else if (parameterType == Short.class) {
				return number.shortValue();
			} else if (parameterType == Long.class) {
				return number.longValue();
			} else if (parameterType == Float.class) {
				return number.floatValue();
			} else if (parameterType == Double.class) {
				return number.doubleValue();
			} else if (parameterType == Byte.class) {
				return number.byteValue();
			}
		} else if (parameterType == Boolean.class) {
			return value.toString();
		} else if (parameterType == Character.class) {
			return value.toString().charAt(0);
		}
		return null;
	}

	/**
	 * 判斷是否為基本數據類型包裝類
	 */
	@SuppressWarnings("rawtypes")
	private boolean isBasicDataTypes(Class clazz) {
		Set<Class> classSet = new HashSet<>();
		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);
		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;
	}
}

三、controller中使用

@PostMapping("/addByMulti")
	public ResponseData addByMulti(@MultiRequestBody("company") Company company, @MultiRequestBody("computer") List<Computer> computer,
			@MultiRequestBody("liaison") List<Liaison> liaison) {
		return companyService.addByMulti(company, computer, liaison);
	}

四、總結

由於時間原因,尚未類的及優化,只對原博主的基礎上加一個List的轉換步驟,有空繼續優化一把。還有如果cotroller中不加value會報錯的。日後再優化改進一把。

參考:https://blog.csdn.net/w605283073/article/details/82119284

 

最近遇到Controller中需要多個@RequestBody的情況,但是發現並不支持這種寫法,

這樣導致

1、單個字符串等包裝類型都要寫一個對象才可以用@RequestBody接收;

2、多個對象需要封裝到一個對象里才可以用@RequestBody接收。

查閱StackOverFlow,受到一個解決方案的啟發,本人改進為以下版本,並給出了詳盡的注釋,希望對大家有幫助。

改進后的方案支持:

1、支持通過注解的value指定JSON的key來解析對象。

2、支持通過注解無value,直接根據參數名來解析對象

3、支持GET方式和其他請求方式

4、支持基本類型屬性注入

5、支持通過注解無value且參數名不匹配JSON串key時,根據屬性解析對象。

6、支持多余屬性(不解析、不報錯)、支持參數“共用”(不指定value時,參數名不為JSON串的key)

7、支持當value和屬性名找不到匹配的key時,對象是否匹配所有屬性。

重要更新記錄:

2019年02月25日 新增xml方式參考配置

2019年02月07日 fix 當list參數為空時,parameterType.newInstance會導致異常。

2018年12月28日 新增測試用例,完善解析部分代碼

2018年10月23日 完善項目格式

2018年08月28日 創建第一版

項目僅供參考,如因使用不當造成任何問題,請自行負責,有問題歡迎探討改進。

項目地址(建議去拉最新代碼):

https://github.com/chujianyun/Spring-MultiRequestBody

另外代碼應該會盡量持續更新完善,歡迎大家貢獻代碼。

 

步驟如下:

0、除spring的Jar包外涉及的主要Maven依賴

  1.  
    <dependency>
  2.  
    <groupId>commons-lang</groupId>
  3.  
    <artifactId>commons-lang</artifactId>
  4.  
    <version>2.4</version>
  5.  
    </dependency>
  6.  
     
  7.  
    <dependency>
  8.  
    <groupId>com.alibaba</groupId>
  9.  
    <artifactId>fastjson</artifactId>
  10.  
    <version>1.2.35</version>
  11.  
    </dependency>
  12.  
     
  13.  
    <dependency>
  14.  
    <groupId>commons-io</groupId>
  15.  
    <artifactId>commons-io</artifactId>
  16.  
    <version>2.6</version>
  17.  
    </dependency>

其中fastjson用來解析json對象,commons-lang用來字符串判空(也可以自己手寫),commons-io用來讀取請求封裝為字符串類型(也可以自己封裝)。

 

1、重寫方法參數解析器

  1.  
    package com.chujianyun.web.bean;
  2.  
     
  3.  
    import com.alibaba.fastjson.JSON;
  4.  
    import com.alibaba.fastjson.JSONObject;
  5.  
    import io.github.chujianyun.annotation.MultiRequestBody;
  6.  
    import org.apache.commons.io.IOUtils;
  7.  
    import org.apache.commons.lang3.StringUtils;
  8.  
    import org.springframework.core.MethodParameter;
  9.  
    import org.springframework.web.bind.support.WebDataBinderFactory;
  10.  
    import org.springframework.web.context.request.NativeWebRequest;
  11.  
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
  12.  
    import org.springframework.web.method.support.ModelAndViewContainer;
  13.  
     
  14.  
    import javax.servlet.http.HttpServletRequest;
  15.  
    import java.io.IOException;
  16.  
    import java.lang.reflect.Field;
  17.  
    import java.util.HashSet;
  18.  
    import java.util.Set;
  19.  
     
  20.  
    /**
  21.  
    * 多RequestBody解析器
  22.  
    *
  23.  
    * @author 明明如月
  24.  
    * @date 2018/08/27
  25.  
    */
  26.  
    public class MultiRequestBodyArgumentResolver implements HandlerMethodArgumentResolver {
  27.  
     
  28.  
    private static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY";
  29.  
     
  30.  
    /**
  31.  
    * 設置支持的方法參數類型
  32.  
    *
  33.  
    * @param parameter 方法參數
  34.  
    * @return 支持的類型
  35.  
    */
  36.  
    @Override
  37.  
    public boolean supportsParameter(MethodParameter parameter) {
  38.  
    // 支持帶@MultiRequestBody注解的參數
  39.  
    return parameter.hasParameterAnnotation(MultiRequestBody.class);
  40.  
    }
  41.  
     
  42.  
    /**
  43.  
    * 參數解析,利用fastjson
  44.  
    * 注意:非基本類型返回null會報空指針異常,要通過反射或者JSON工具類創建一個空對象
  45.  
    */
  46.  
    @Override
  47.  
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
  48.  
     
  49.  
    String jsonBody = getRequestBody(webRequest);
  50.  
     
  51.  
    JSONObject jsonObject = JSON.parseObject(jsonBody);
  52.  
    // 根據@MultiRequestBody注解value作為json解析的key
  53.  
    MultiRequestBody parameterAnnotation = parameter.getParameterAnnotation(MultiRequestBody.class);
  54.  
    //注解的value是JSON的key
  55.  
    String key = parameterAnnotation.value();
  56.  
    Object value;
  57.  
    // 如果@MultiRequestBody注解沒有設置value,則取參數名FrameworkServlet作為json解析的key
  58.  
    if (StringUtils.isNotEmpty(key)) {
  59.  
    value = jsonObject.get(key);
  60.  
    // 如果設置了value但是解析不到,報錯
  61.  
    if (value == null && parameterAnnotation.required()) {
  62.  
    throw new IllegalArgumentException(String.format("required param %s is not present", key));
  63.  
    }
  64.  
    } else {
  65.  
    // 注解為設置value則用參數名當做json的key
  66.  
    key = parameter.getParameterName();
  67.  
    value = jsonObject.get(key);
  68.  
    }
  69.  
     
  70.  
    // 獲取的注解后的類型 Long
  71.  
    Class<?> parameterType = parameter.getParameterType();
  72.  
    // 通過注解的value或者參數名解析,能拿到value進行解析
  73.  
    if (value != null) {
  74.  
    //基本類型
  75.  
    if (parameterType.isPrimitive()) {
  76.  
    return parsePrimitive(parameterType.getName(), value);
  77.  
    }
  78.  
    // 基本類型包裝類
  79.  
    if (isBasicDataTypes(parameterType)) {
  80.  
    return parseBasicTypeWrapper(parameterType, value);
  81.  
    // 字符串類型
  82.  
    } else if (parameterType == String.class) {
  83.  
    return value.toString();
  84.  
    }
  85.  
    // 其他復雜對象
  86.  
    return JSON.parseObject(value.toString(), parameterType);
  87.  
    }
  88.  
     
  89.  
    // 解析不到則將整個json串解析為當前參數類型
  90.  
    if (isBasicDataTypes(parameterType)) {
  91.  
    if (parameterAnnotation.required()) {
  92.  
    throw new IllegalArgumentException(String.format("required param %s is not present", key));
  93.  
    } else {
  94.  
    return null;
  95.  
    }
  96.  
    }
  97.  
     
  98.  
    // 非基本類型,不允許解析所有字段,必備參數則報錯,非必備參數則返回null
  99.  
    if (!parameterAnnotation.parseAllFields()) {
  100.  
    // 如果是必傳參數拋異常
  101.  
    if (parameterAnnotation.required()) {
  102.  
    throw new IllegalArgumentException(String.format("required param %s is not present", key));
  103.  
    }
  104.  
    // 否則返回null
  105.  
    return null;
  106.  
    }
  107.  
    // 非基本類型,允許解析,將外層屬性解析
  108.  
    Object result;
  109.  
    try {
  110.  
    result = JSON.parseObject(jsonObject.toString(), parameterType);
  111.  
    } catch (JSONException jsonException) {
  112.  
    // TODO:: 異常處理返回null是否合理?
  113.  
    result = null;
  114.  
    }
  115.  
     
  116.  
    // 如果非必要參數直接返回,否則如果沒有一個屬性有值則報錯
  117.  
    if (!parameterAnnotation.required()) {
  118.  
    return result;
  119.  
    } else {
  120.  
    boolean haveValue = false;
  121.  
    Field[] declaredFields = parameterType.getDeclaredFields();
  122.  
    for (Field field : declaredFields) {
  123.  
    field.setAccessible( true);
  124.  
    if (field.get(result) != null) {
  125.  
    haveValue = true;
  126.  
    break;
  127.  
    }
  128.  
    }
  129.  
    if (!haveValue) {
  130.  
    throw new IllegalArgumentException(String.format("required param %s is not present", key));
  131.  
    }
  132.  
    return result;
  133.  
    }
  134.  
    }
  135.  
     
  136.  
    /**
  137.  
    * 基本類型解析
  138.  
    */
  139.  
    private Object parsePrimitive(String parameterTypeName, Object value) {
  140.  
    final String booleanTypeName = "boolean";
  141.  
    if (booleanTypeName.equals(parameterTypeName)) {
  142.  
    return Boolean.valueOf(value.toString());
  143.  
    }
  144.  
    final String intTypeName = "int";
  145.  
    if (intTypeName.equals(parameterTypeName)) {
  146.  
    return Integer.valueOf(value.toString());
  147.  
    }
  148.  
    final String charTypeName = "char";
  149.  
    if (charTypeName.equals(parameterTypeName)) {
  150.  
    return value.toString().charAt(0);
  151.  
    }
  152.  
    final String shortTypeName = "short";
  153.  
    if (shortTypeName.equals(parameterTypeName)) {
  154.  
    return Short.valueOf(value.toString());
  155.  
    }
  156.  
    final String longTypeName = "long";
  157.  
    if (longTypeName.equals(parameterTypeName)) {
  158.  
    return Long.valueOf(value.toString());
  159.  
    }
  160.  
    final String floatTypeName = "float";
  161.  
    if (floatTypeName.equals(parameterTypeName)) {
  162.  
    return Float.valueOf(value.toString());
  163.  
    }
  164.  
    final String doubleTypeName = "double";
  165.  
    if (doubleTypeName.equals(parameterTypeName)) {
  166.  
    return Double.valueOf(value.toString());
  167.  
    }
  168.  
    final String byteTypeName = "byte";
  169.  
    if (byteTypeName.equals(parameterTypeName)) {
  170.  
    return Byte.valueOf(value.toString());
  171.  
    }
  172.  
    return null;
  173.  
    }
  174.  
     
  175.  
    /**
  176.  
    * 基本類型包裝類解析
  177.  
    */
  178.  
    private Object parseBasicTypeWrapper(Class<?> parameterType, Object value) {
  179.  
    if (Number.class.isAssignableFrom(parameterType)) {
  180.  
    Number number = (Number) value;
  181.  
    if (parameterType == Integer.class) {
  182.  
    return number.intValue();
  183.  
    } else if (parameterType == Short.class) {
  184.  
    return number.shortValue();
  185.  
    } else if (parameterType == Long.class) {
  186.  
    return number.longValue();
  187.  
    } else if (parameterType == Float.class) {
  188.  
    return number.floatValue();
  189.  
    } else if (parameterType == Double.class) {
  190.  
    return number.doubleValue();
  191.  
    } else if (parameterType == Byte.class) {
  192.  
    return number.byteValue();
  193.  
    }
  194.  
    } else if (parameterType == Boolean.class) {
  195.  
    return value.toString();
  196.  
    } else if (parameterType == Character.class) {
  197.  
    return value.toString().charAt(0);
  198.  
    }
  199.  
    return null;
  200.  
    }
  201.  
     
  202.  
    /**
  203.  
    * 判斷是否為基本數據類型包裝類
  204.  
    */
  205.  
    private boolean isBasicDataTypes(Class clazz) {
  206.  
    Set<Class> classSet = new HashSet<>();
  207.  
    classSet.add(Integer.class);
  208.  
    classSet.add(Long.class);
  209.  
    classSet.add(Short.class);
  210.  
    classSet.add(Float.class);
  211.  
    classSet.add(Double.class);
  212.  
    classSet.add(Boolean.class);
  213.  
    classSet.add(Byte.class);
  214.  
    classSet.add(Character.class);
  215.  
    return classSet.contains(clazz);
  216.  
    }
  217.  
     
  218.  
    /**
  219.  
    * 獲取請求體JSON字符串
  220.  
    */
  221.  
    private String getRequestBody(NativeWebRequest webRequest) {
  222.  
    HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
  223.  
     
  224.  
    // 有就直接獲取
  225.  
    String jsonBody = (String) webRequest.getAttribute(JSONBODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
  226.  
    // 沒有就從請求中讀取
  227.  
    if (jsonBody == null) {
  228.  
    try {
  229.  
    jsonBody = IOUtils.toString(servletRequest.getReader());
  230.  
    webRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
  231.  
    } catch (IOException e) {
  232.  
    throw new RuntimeException(e);
  233.  
    }
  234.  
    }
  235.  
    return jsonBody;
  236.  
    }
  237.  
    }

 

2、編寫解析的方法注解:

  1.  
    package com.chujianyun.web.annotation;
  2.  
     
  3.  
    import java.lang.annotation.ElementType;
  4.  
    import java.lang.annotation.Retention;
  5.  
    import java.lang.annotation.RetentionPolicy;
  6.  
    import java.lang.annotation.Target;
  7.  
     
  8.  
    /**
  9.  
    * Controller中方法接收多個JSON對象
  10.  
    *
  11.  
    * @author 明明如月
  12.  
    * @date 2018/08/27
  13.  
    */
  14.  
    @Target(ElementType.PARAMETER)
  15.  
    @Retention(RetentionPolicy.RUNTIME)
  16.  
    public @interface MultiRequestBody {
  17.  
    /**
  18.  
    * 是否必須出現的參數
  19.  
    */
  20.  
    boolean required() default true;
  21.  
     
  22.  
    /**
  23.  
    * 當value的值或者參數名不匹配時,是否允許解析最外層屬性到該對象
  24.  
    */
  25.  
    boolean parseAllFields() default true;
  26.  
     
  27.  
    /**
  28.  
    * 解析時用到的JSON的key
  29.  
    */
  30.  
    String value() default "";
  31.  
    }

3、在配置Bean中注入

特別注意: 如果加入本配置導致頁面訪問404 可以去掉 @EnableWebMvc注解

  1.  
    package com.chujianyun.web.config;
  2.  
     
  3.  
    import com.chujianyun.web.bean.MultiRequestBodyArgumentResolver;
  4.  
    import org.springframework.context.annotation.Bean;
  5.  
    import org.springframework.context.annotation.Configuration;
  6.  
    import org.springframework.http.converter.HttpMessageConverter;
  7.  
    import org.springframework.http.converter.StringHttpMessageConverter;
  8.  
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
  9.  
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  10.  
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
  11.  
     
  12.  
    import java.nio.charset.Charset;
  13.  
    import java.util.List;
  14.  
     
  15.  
    /**
  16.  
    * 添加多RequestBody解析器
  17.  
    * @author 明明如月
  18.  
    * @date 2018/08/27
  19.  
    */
  20.  
    @Configuration
  21.  
    @EnableWebMvc
  22.  
    public class WebConfig extends WebMvcConfigurerAdapter {
  23.  
    @Override
  24.  
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
  25.  
    argumentResolvers.add( new MultiRequestBodyArgumentResolver());
  26.  
    }
  27.  
     
  28.  
    @Bean
  29.  
    public HttpMessageConverter<String> responseBodyConverter() {
  30.  
    return new StringHttpMessageConverter(Charset.forName("UTF-8"));
  31.  
    }
  32.  
     
  33.  
    @Override
  34.  
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  35.  
    super.configureMessageConverters(converters);
  36.  
    converters.add(responseBodyConverter());
  37.  
    }
  38.  
    }

 

xml配置方式(感謝網友 "熔 岩"提供了的xml參考配置方式)

  1.  
    <mvc:annotation-driven>
  2.  
    <mvc:message-converters>
  3.  
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
  4.  
    <constructor-arg value="UTF-8"/>
  5.  
    </bean>
  6.  
    <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
  7.  
    <property name="supportedMediaTypes">
  8.  
    <list>
  9.  
    <value>application/json</value>
  10.  
    <value>text/html</value>
  11.  
    <value>text/plain</value>
  12.  
    </list>
  13.  
    </property>
  14.  
    <property name="fastJsonConfig" ref="fastJsonConfig"/>
  15.  
    </bean>
  16.  
    </mvc:message-converters>
  17.  
     
  18.  
    <mvc:argument-resolvers>
  19.  
    <bean class="io.github.chujianyun.bean.MultiRequestBodyArgumentResolver"/>
  20.  
    </mvc:argument-resolvers>
  21.  
    </mvc:annotation-driven>

使用方法:

  1.  
    package com.chujianyun.web.controller;
  2.  
     
  3.  
    import com.chujianyun.web.annotation.MultiRequestBody;
  4.  
    import com.chujianyun.web.domain.Dog;
  5.  
    import com.chujianyun.web.domain.User;
  6.  
    import org.springframework.stereotype.Controller;
  7.  
    import org.springframework.web.bind.annotation.RequestMapping;
  8.  
    import org.springframework.web.bind.annotation.ResponseBody;
  9.  
     
  10.  
    /**
  11.  
    * 演示控制器
  12.  
    * @author 明明如月
  13.  
    * @date 2018/08/27
  14.  
    */
  15.  
    @Controller
  16.  
    @RequestMapping("/xhr/test")
  17.  
    public class DemoController {
  18.  
     
  19.  
    @RequestMapping("/demo")
  20.  
    @ResponseBody
  21.  
    public String multiRequestBodyDemo1(@MultiRequestBody Dog dog, @MultiRequestBody User user) {
  22.  
    System.out.println(dog.toString()+user.toString());
  23.  
    return dog.toString()+";"+user.toString();
  24.  
    }
  25.  
     
  26.  
     
  27.  
    @RequestMapping("/demo2")
  28.  
    @ResponseBody
  29.  
    public String multiRequestBodyDemo2(@MultiRequestBody("dog") Dog dog, @MultiRequestBody User user) {
  30.  
    System.out.println(dog.toString()+user.toString());
  31.  
    return dog.toString()+";"+user.toString();
  32.  
    }
  33.  
     
  34.  
    @RequestMapping("/demo3")
  35.  
    @ResponseBody
  36.  
    public String multiRequestBodyDemo3(@MultiRequestBody("dog") Dog dog, @MultiRequestBody("user") User user) {
  37.  
    System.out.println(dog.toString()+user.toString());
  38.  
    return dog.toString()+";"+user.toString();
  39.  
    }
  40.  
     
  41.  
     
  42.  
     
  43.  
    @RequestMapping("/demo4")
  44.  
    @ResponseBody
  45.  
    public String multiRequestBodyDemo4(@MultiRequestBody("dog") Dog dog, @MultiRequestBody Integer age) {
  46.  
    System.out.println(dog.toString() + age.toString());
  47.  
    return dog.toString() + ";age屬性為:"+age.toString();
  48.  
    }
  49.  
     
  50.  
     
  51.  
    @RequestMapping("/demo5")
  52.  
    @ResponseBody
  53.  
    public String multiRequestBodyDemo5(@MultiRequestBody("color") String color, @MultiRequestBody("age") Integer age) {
  54.  
    return "color="+color + "; age=" + age;
  55.  
    }
  56.  
     
  57.  
    @RequestMapping("/demo6")
  58.  
    @ResponseBody
  59.  
    public String multiRequestBodyDemo6(@MultiRequestBody("dog") Dog dog, @MultiRequestBody Integer age) {
  60.  
    System.out.println(dog.toString() + age.toString());
  61.  
    return dog.toString() + ";age屬性為:"+age.toString();
  62.  
    }
  63.  
     
  64.  
     
  65.  
    @RequestMapping("/demo7")
  66.  
    @ResponseBody
  67.  
    public String multiRequestBodyDemo7(@MultiRequestBody Dog color2, @MultiRequestBody("age") Integer age) {
  68.  
    return "color="+color2 + "; age=" + age;
  69.  
    }
  70.  
     
  71.  
     
  72.  
    @RequestMapping("/demo9")
  73.  
    @ResponseBody
  74.  
    public String multiRequestBodyDemo9( @MultiRequestBody Dog dog) {
  75.  
    return dog.toString();
  76.  
    }
  77.  
    @RequestMapping("/demo10")
  78.  
    @ResponseBody
  79.  
    public String multiRequestBodyDemo10( @MultiRequestBody(parseAllFields = false,required = false) Dog dog) {
  80.  
    return dog.toString();
  81.  
    }
  82.  
     
  83.  
    @RequestMapping("/testList")
  84.  
    @ResponseBody
  85.  
    public String multiRequestBodyDemo1(@MultiRequestBody List test, @MultiRequestBody String str) {
  86.  
     
  87.  
    return test.toString() + str;
  88.  
    }
  89.  
     
  90.  
    }

兩個實體:

  1.  
    package com.chujianyun.web.domain;
  2.  
     
  3.  
    /**
  4.  
    * @author 明明如月
  5.  
    * @date 2018/08/27
  6.  
    */
  7.  
    public class Dog {
  8.  
     
  9.  
    private String name;
  10.  
     
  11.  
    private String color;
  12.  
     
  13.  
     
  14.  
    public String getName() {
  15.  
    return name;
  16.  
    }
  17.  
     
  18.  
    public void setName(String name) {
  19.  
    this.name = name;
  20.  
    }
  21.  
     
  22.  
    public String getColor() {
  23.  
    return color;
  24.  
    }
  25.  
     
  26.  
    public void setColor(String color) {
  27.  
    this.color = color;
  28.  
    }
  29.  
     
  30.  
    @Override
  31.  
    public String toString() {
  32.  
    return "Dog{" +
  33.  
    "name='" + name + '\'' +
  34.  
    ", color='" + color + '\'' +
  35.  
    '}';
  36.  
    }
  37.  
    }
  1.  
    package com.chujianyun.web.domain;
  2.  
     
  3.  
    /**
  4.  
    * @author 明明如月
  5.  
    * @date 2018/08/27
  6.  
    */
  7.  
    public class User {
  8.  
     
  9.  
    private String name;
  10.  
     
  11.  
    private Integer age;
  12.  
     
  13.  
    public String getName() {
  14.  
    return name;
  15.  
    }
  16.  
     
  17.  
    public void setName(String name) {
  18.  
    this.name = name;
  19.  
    }
  20.  
     
  21.  
    public Integer getAge() {
  22.  
    return age;
  23.  
    }
  24.  
     
  25.  
    public void setAge(Integer age) {
  26.  
    this.age = age;
  27.  
    }
  28.  
     
  29.  
    @Override
  30.  
    public String toString() {
  31.  
    return "User{" +
  32.  
    "name='" + name + '\'' +
  33.  
    ", age=" + age +
  34.  
    '}';
  35.  
    }
  36.  
    }

效果:

demo 

demo2

demo3

 

參考文章:https://stackoverflow.com/questions/12893566/passing-multiple-variables-in-requestbody-to-a-spring-mvc-controller-using-ajax

https://blog.csdn.net/w605283073/article/details/82119284/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM