目录

Java-Web前端利用-form-表单传多项数据,后端-Servlet-取出的各项数据均为空

【Java Web】前端利用 form 表单传多项数据,后端 Servlet 取出的各项数据均为空

前端利用 form 表单传多项数据,后端 Servlet 取出的各项数据均为空

文章目录

1.问题引入

最近在写一个 java web 项目时,遇到一个让我头疼了一下午的问题:前端通过 post 提交的 form 表单数据可以传到后端,但当我从 Servlet 中通过 request.getParameter(“name”) 拿取各项数据时,得到的内容均为 Null !

为便于读者理解,我先描述下当前的业务场景:弹窗界面用于提示录入用户信息,含姓名、学位和照片(这些信息将被组织在一个 form 表单中)。在点击 “Add” 后,该 form 表单会提交全部数据至后台,并将这些数据存放至数据库中。

https://i-blog.csdnimg.cn/blog_migrate/bef2765a4f9066f7e6abe87127e83aa8.png#pic_center

下面是这部分内容对应的代码(为便于读者快速进入题境,我省略了一些无关的头、尾部代码,并保证这部分代码在该问题中未造成任何影响):

前端代码 (仅展示 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 {

    @Override
    protected 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 方法能根据系统中已有的用户信息自动创建唯一的 ID
        String 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:

https://i-blog.csdnimg.cn/blog_migrate/72eaffddeefb61bb57b4a5c65b551a46.png#pic_center

注:上面的报错是由于 String degreeIndex = req.getParameter(“degree”); 得到了 null ,因此在后续 Degree.get(Integer.parseInt(degreeIndex)) 时会出现 “对 null 强制转换为 int 型” 的错。

最关键的来了!当我用 F12 在浏览器中查看包的信息时,发现 post 请求中的确含有 name、degree 和 photo 的数据!!!

https://i-blog.csdnimg.cn/blog_migrate/7c296a6132d3f94d08a7d4bcce0a4d5c.png#pic_center

于是,头疼开始了!

但是我们可以肯定一件事: form 表单完成了对表内数据的传送,问题在于 Servlet 无法取出!


2.问题解决

这时候通过网上查询,基本可以得到以下排错手段(参考博客: ):

1、form 表单中未写 name 属性;

2、jsp 的提交方式与 servlet 不一致(如:在 jsp 中用的 post ,但是在对应 servlet 中写的是 doGet);

3、form 表单的 enctype 属性与 servlet 不一致;

4、servlet 中的 getParameter() 参数与 form 表单不一致。

但遗憾的是,我都正确配置了这些参数,可取出的就是 null 。

于是,局面开始变得焦灼起来。

直到有一刻,当我在 CSDN 漫步时,无意点开了一个博客: 。因为当时已经看了很多博文,所以在看这些文章时基本都是走马观花式的,非常快,一扫而过的那种。我记得很清楚,当点开这篇博文的第一刻,我直接一眼扫到一个词汇,并当即顿悟:

https://i-blog.csdnimg.cn/blog_migrate/6e1b17d43aa322ffe3021163ed580062.png#pic_center

我想,当你看到这张图片时应该和我一样,直接灵光乍现了吧?

在前端用了 multipart/form-data 封装 form 表单数据,后端接受到的数据是一个含文本、二进制数据的复杂数据对象,这时候肯定不能直接通过 getParameter() 获取。因为 getParameter() 方法是不能对这种 “打包数据对象” 进行解析的。这时候最简单的解决办法就是在 servlet 处添加 @MultipartConfig 注解,以告知 servlet,此时接受到的数据是同时含文本、二进制数据的,需要在 getParameter() 前进行适当预处理。这样一来, getParameter() 才能从前端发来的数据对象中正确解析出各项 name 对应的值。

所以,正确的后端代码应改为:

@WebServlet("/PersonAddServlet")
@MultipartConfig
public class PersonModifyServlet extends HttpServlet {

    @Override
    protected 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 方法能根据系统中已有的用户信息自动创建唯一的 ID
        String 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 注解。

这时,所有的数据均能正确地取出!

说实话这是一个很隐秘的错,踩坑一次,绝不再犯,特此记录~~~

END