前端利用 form 表单传多项数据,后端 Servlet 取出的各项数据均为空
文章目录
- 1.问题引入
- 2.问题解决
1.问题引入
最近在写一个 java web 项目时,遇到一个让我头疼了一下午的问题:前端通过 post 提交的 form 表单数据可以传到后端,但当我从 Servlet 中通过 request.getParameter(“name”) 拿取各项数据时,得到的内容均为 Null !
为便于读者理解,我先描述下当前的业务场景:弹窗界面用于提示录入用户信息,含姓名、学位和照片(这些信息将被组织在一个 form 表单中)。在点击 “Add” 后,该 form 表单会提交全部数据至后台,并将这些数据存放至数据库中。
下面是这部分内容对应的代码(为便于读者快速进入题境,我省略了一些无关的头、尾部代码,并保证这部分代码在该问题中未造成任何影响):
前端代码(仅展示 form 表单和对应的 JS 代码):
<div style="height: 400px; overflow-y: auto; margin-bottom: 60px; padding: 20px"><form name="personForm" class="layui-form layui-form-pane" style="padding: 5px" enctype="multipart/form-data"><div class="layui-form-item"><label class="layui-form-label">Name</label><div class="layui-input-block"><input type="text" id="name" name="name" class="layui-input" /></div></div><div class="layui-form-item"><label class="layui-form-label">Degree</label><div class="layui-input-inline"><select id="degree" name="degree" size="3"><option value="0">B.S.</option><option value="1">M.S.</option><option value="2">Dr.</option></c:if></select></div></div><div class="layui-form-item"><label class="layui-form-label">Photo</label><div class="layui-input-block"><input type="file" name="photo" id="photo"style="position: relative;display: inline-block;background: #21997f;border: 1px solid #0e8050;border-radius: 4px;padding: 5px 12px;overflow: hidden;color: #ffffff;text-decoration: none;text-indent: 0;line-height: 25px;"></div></div><div class="layui-form-item"><button class="layui-btn" style="display:block; margin-left:auto; margin-right:auto; margin-top: 1rem;"lay-submit lay-filter="add" id="addpersopn" onclick="addPerson()">Modify</button></div></form></div>
<script type="text/javascript">function addPerson() {var name = $("#name").val();var selectElement = document.getElementById("degree");var degree = selectElement.options[selectElement.selectedIndex].value;if (name == "") {alert("Please fill name!")return;} else if (degree == "") {alert("Please select degree!")return;} else {console.log("Get input message:")console.log(name)console.log(degree)personForm.method = "post";personForm.action = "PersonAddServlet";personForm.submit();}}
</script>
后端代码
@WebServlet("/PersonAddServlet")
public class PersonModifyServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("\n*----------------------------- PersonAddServlet Log ----------------------------*");System.out.println("| |");System.out.println("| Enter PersonAddServlet |");System.out.println("| |");System.out.println("| |");// 转码resp.setContentType("text/html; charset=UTF-8");// 设置请求编码req.setCharacterEncoding("UTF-8");// 得到输出流PrintWriter out = resp.getWriter();// IDTool 是一个我已实现的静态类, createID 方法能根据系统中已有的用户信息自动创建唯一的 IDString personID = IDTool.createID(1);// 获取 form 表单的数据// 姓名String name = req.getParameter("name");System.out.println("| Get name: " + name);// 学位String degreeIndex = req.getParameter("degree");List<String> Degree = new ArrayList<>();Degree.add("B.S.");Degree.add("M.S.");Degree.add("Dr.");String degree = Degree.get(Integer.parseInt(degreeIndex));System.out.println("| Get degree: " + degree);// 照片Part part = req.getPart("photo");String photo = null;// 获取待上传位置String realPath = req.getServletContext().getRealPath("/" + DirConfig.getFILEHOME() + "/" + DirConfig.getIMAGEDIR() + "/");// 拼接照片文件的上传地址photo = realPath + part .getSubmittedFileName();// 上传照片part.write(photo);// 将用户信息写入数据库(PersonRepository 是我已实现的用于和数据库进行数据交互的类)PersonRepository personRepository = new PersonRepository();if(personRepository.addPerson(personID, name, degree, photo)){out.flush();out.println("<script>");out.println("alert('Successfully updated personal information!');");out.println("top.location.href='manage.jsp';");out.println("</script>");}else{out.flush();out.println("<script>");out.println("alert('Updated personal information failed!');");out.println("top.location.href='manage.jsp';");out.println("</script>");}System.out.println("| |");System.out.println("| |");System.out.println("*----------------------------- PersonAddServlet END ----------------------------*\n");}
}
以上代码的逻辑很清晰:
1、前端获取输入;
2、后端拿取输入,并完成添加。
但在运行时,后端 Servlet 取出的 name 和 degree 始终为 null:
注:上面的报错是由于 String degreeIndex = req.getParameter(“degree”); 得到了 null ,因此在后续 Degree.get(Integer.parseInt(degreeIndex)) 时会出现 “对 null 强制转换为 int 型” 的错。
最关键的来了!当我用 F12 在浏览器中查看包的信息时,发现 post 请求中的确含有 name、degree 和 photo 的数据!!!
于是,头疼开始了!
但是我们可以肯定一件事:form 表单完成了对表内数据的传送,问题在于 Servlet 无法取出!
2.问题解决
这时候通过网上查询,基本可以得到以下排错手段(参考博客:jsp提交form表单到servlet,但取值全为空):
1、form 表单中未写 name 属性;
2、jsp 的提交方式与 servlet 不一致(如:在 jsp 中用的 post ,但是在对应 servlet 中写的是 doGet);
3、form 表单的 enctype 属性与 servlet 不一致;
4、servlet 中的 getParameter() 参数与 form 表单不一致。
但遗憾的是,我都正确配置了这些参数,可取出的就是 null 。
于是,局面开始变得焦灼起来。
直到有一刻,当我在 CSDN 漫步时,无意点开了一个博客:【JAVA】前端multipart/form-data传文件及参数,后端拿值问题。因为当时已经看了很多博文,所以在看这些文章时基本都是走马观花式的,非常快,一扫而过的那种。我记得很清楚,当点开这篇博文的第一刻,我直接一眼扫到一个词汇,并当即顿悟:
我想,当你看到这张图片时应该和我一样,直接灵光乍现了吧?
在前端用了 multipart/form-data 封装 form 表单数据,后端接受到的数据是一个含文本、二进制数据的复杂数据对象,这时候肯定不能直接通过 getParameter() 获取。因为 getParameter() 方法是不能对这种 “打包数据对象” 进行解析的。这时候最简单的解决办法就是在 servlet 处添加 @MultipartConfig 注解,以告知 servlet,此时接受到的数据是同时含文本、二进制数据的,需要在 getParameter() 前进行适当预处理。这样一来, getParameter() 才能从前端发来的数据对象中正确解析出各项 name 对应的值。
所以,正确的后端代码应改为:
@WebServlet("/PersonAddServlet")
@MultipartConfig
public class PersonModifyServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("\n*----------------------------- PersonAddServlet Log ----------------------------*");System.out.println("| |");System.out.println("| Enter PersonAddServlet |");System.out.println("| |");System.out.println("| |");// 转码resp.setContentType("text/html; charset=UTF-8");// 设置请求编码req.setCharacterEncoding("UTF-8");// 得到输出流PrintWriter out = resp.getWriter();// IDTool 是一个我已实现的静态类, createID 方法能根据系统中已有的用户信息自动创建唯一的 IDString personID = IDTool.createID(1);// 获取 form 表单的数据// 姓名String name = req.getParameter("name");System.out.println("| Get name: " + name);// 学位String degreeIndex = req.getParameter("degree");List<String> Degree = new ArrayList<>();Degree.add("B.S.");Degree.add("M.S.");Degree.add("Dr.");String degree = Degree.get(Integer.parseInt(degreeIndex));System.out.println("| Get degree: " + degree);// 照片Part part = req.getPart("photo");String photo = null;// 获取待上传位置String realPath = req.getServletContext().getRealPath("/" + DirConfig.getFILEHOME() + "/" + DirConfig.getIMAGEDIR() + "/");// 拼接照片文件的上传地址photo = realPath + part .getSubmittedFileName();// 上传照片part.write(photo);// 将用户信息写入数据库(PersonRepository 是我已实现的用于和数据库进行数据交互的类)PersonRepository personRepository = new PersonRepository();if(personRepository.addPerson(personID, name, degree, photo)){out.flush();out.println("<script>");out.println("alert('Successfully updated personal information!');");out.println("top.location.href='manage.jsp';");out.println("</script>");}else{out.flush();out.println("<script>");out.println("alert('Updated personal information failed!');");out.println("top.location.href='manage.jsp';");out.println("</script>");}System.out.println("| |");System.out.println("| |");System.out.println("*----------------------------- PersonAddServlet END ----------------------------*\n");}
}
即:添加 @MultipartConfig 注解。
这时,所有的数据均能正确地取出!
说实话这是一个很隐秘的错,踩坑一次,绝不再犯,特此记录~~~