陈伟华

前后端分离。前端POST请求参数过长,导致400错误解决办法及分析

这两天做好的功能要上线了。但是测试的时候忽然发现当POST提交数据量多大时,会导致后端报400错误。最开始以为瓶颈存在于tomcat,因为tomcat默认能接受的POST请求大小为2M,所以手动修改tomcat server.xml 文件,将接受POST大小修改为不限制。 经测试,发现依旧不起作用。 今天上网查询了相关资料,解决了这个问题。资料比较零散,在这里整理一下,为同样遇到该问题的伙伴们提供一个解决思路。也为自己以后再遇到这种问题做个笔记。

 

首先看了一下前端提交的Content-type 发现是application/x-www-form-urlencoded  然后就根据这个去查找相关问题,找到如下文章:

http://hongjiang.info/http-application-x-www-form-urlencoded/#comments

里面介绍的有几句话起到了很大的作用:

 

同事遇到在servlet端通过request对象getInputStream读取POST过来的数据,却读不到的问题,怀疑是tomcat的问题。查了一下Content-typeapplication/x-www-form-urlencoded,估计是被解析成了parameters,果然在他获取流之前,有过request.getParameter的操作。

熟悉servlet的话,这个问题应该算常识了。它其实跟容器无关,所有的servlet容器都是这样的行为。几年前在实现一个网关代理的时候就遇到过这个问题,当时使用的是jetty,发现POST过来的数据读不到,也是application/x-www-form-urlencoded编码,断点跟踪发现是在获取流之前有过request.getParameter,数据会被解析,并且后续数据流不可再被读取。

在servlet规范3.1.1节里,对POST数据何时会被当做parameters有描述:

1. The request is an HTTP or HTTPS request.
2. The HTTP method is POST.
3. The content type is application/x-www-form-urlencoded.
4. The servlet has made an initial call of any of the getParameter family of methods on the request object.

If the conditions are met, post form data will no longer be available for reading directly from the request object’s input stream.


规范里已经明确的声明当请求满足: 1) http/https, 2) POST, 3) Content-type 是application/x-www-form-urlencoded, 4) 调用过getParameter方法;则数据会被当做请求的paramaters,而不能再通过 request 的 inputstream 直接读取。

这里给了我很大的思路,说是POST提交,实则在我spring mvc中是这么写的:

 

/**
	 * 保存盘点记录
	 * 
	 * @param data
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value = "/saveStoreCheck", method = RequestMethod.POST)
	public AjaxResult saveStoreCheck(String data) {
		GoodsJson materials = new Gson().fromJson(data, InventoryCheckIn.class);
		inventoryCheckIn.setCheckId(ShopUUID.getUUID(ShopUUID.UUID_CHECK));
		inventoryCheckIn.setKtvId(ShopConstUtil.getShopId());
		try {
			String checkId = inventoryCheckService.saveStoreCheck(inventoryCheckIn);
			return AjaxResult.getOK(checkId);
		} catch (InventoryCheckException | AccessToLibraryException | AllotException e) {
			return AjaxResult.getLogicError(e.getMessage(), "");
		}
	}

 在我看来,这样的写法实际上传过来的参数依旧是会拼成类似于URL参数一样过来。所以当长度过长时,会导致出错。所以就将提交方式 Content-type修改为了application/json 这种JSON格式,不会限制传过来的大小。同时后台接收也就需要修改。代码修改为:

 

/**
	 * 保存盘点记录
	 * 
	 * @param data
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value = "/saveStoreCheck", method = RequestMethod.POST,produces="application/json")
	public AjaxResult saveStoreCheck(@RequestBody InventoryCheck inventoryCheckIn) {
		inventoryCheckIn.setCheckId(ShopUUID.getUUID(ShopUUID.UUID_CHECK));
		inventoryCheckIn.setKtvId(ShopConstUtil.getShopId());
		try {
			String checkId = inventoryCheckService.saveStoreCheck(inventoryCheckIn);
			return AjaxResult.getOK(checkId);
		} catch (InventoryCheckException | AccessToLibraryException | AllotException e) {
			return AjaxResult.getLogicError(e.getMessage(), "");
		}
	}

直接使用对象接收,不需要再进行JSON转换。这样问题就解决了。

posted on 2021-10-26 19:33  陈伟华  阅读(1648)  评论(0编辑  收藏  举报

导航