目录:
- 一. 问题引入
- 二 解决问题
一. 问题引入
近在写一个 java web 项目时,遇到一个让我头疼了晚上的问题:前端通过 post 提交的 form 表单数据可以传到后端,但当我从 Servlet 中通过 request.getParameter(“name”) 拿取各项数据时,得到的内容均为 Null !
为便于读者理解,我先描述下当前的业务场景:弹窗界面用于提示录入合同名称,合同类型和合同文件(这些信息将被组织在一个 form 表单中)。在点击 “归档合同” 后,该 form 表单会提交全部数据至后台,并将这些数据存放至数据库中。
下面是这部分内容对应的代码
前端代码(仅展示 form 表单和对应的 JS 代码):
<!-- Contract archiving form -->
<form id="contractArchivingForm" class="bg-light p-3" action="file.do" method="post" enctype="multipart/form-data" >
<input type="hidden" name="flag" value="insert"/>
<div class="form-group">
<label for="contractName">合同名称</label>
<input type="text" class="form-control" id="contractName" name="contractName" placeholder="输入合同名称" required>
</div>
<div class="form-group">
<label for="contractCategory">合同类别</label>
<select class="form-control" id="contractCategory" name="contractCategory">
<option>租赁协议</option>
<option>销售合同</option>
<option>服务协议</option>
<!-- More contract categories can be added as needed -->
</select>
</div>
<div class="form-group">
<label for="contractFileUpload">上传合同文件</label>
<input type="file" class="form-control-file" id="contractFileUpload" name="contractFileUpload" required>
<small class="form-text text-muted">请上传PDF格式的文件,大小不超过10MB。</small>
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="agreement">
<label class="form-check-label" for="agreement">我确认合同信息准确无误,并同意归档。</label>
</div>
<input type="submit" class="btn btn-primary" value="归档合同" >
</form>
后端代码
package com.jjy.web.servlet;
import com.jjy.pojo.FileInfo;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@WebServlet("/file.do")
public class FileInfoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String flag = req.getParameter("flag");
System.out.println("未进入"+flag);
if ("insert".equals(flag)) {
System.out.println(flag);
insertFileinfo(req, resp);
}
}
private void insertFileinfo(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String contractName = request.getParameter("contractName");
String contractCategory = request.getParameter("contractCategory");
FileInfo fileInfo = new FileInfo();
Part part = request.getPart("contractFileUpload");
//通过Part对象得到上传的文件名
String fileName = part.getSubmittedFileName();
System.out.println("上传文件名:" + fileName);
//得到文件存放的路径
String filePath = request.getServletContext().getRealPath("/");
System.out.println("文件存放路径:" + filePath);
part.write(filePath + "/" + fileName);
fileInfo.setFilename(contractName);
fileInfo.setFiletype(contractCategory);
LocalDateTime currentDateTime = LocalDateTime.now();
// 定义日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 将LocalDateTime转换为String
String formattedDateTime = currentDateTime.format(formatter);
fileInfo.setFiletime(formattedDateTime);
System.out.println(fileInfo);
// 处理完毕后重定向或者显示成功消息
response.sendRedirect("guidang.html");
}
}
以上代码的逻辑很清晰:
1、前端获取输入;
2、后端拿取输入,并完成添加。
但在运行时,后端 Servlet 取出的值始终为 null:
当我用 F12 在浏览器中查看包的信息时,发现 post 请求中的确含数据!!!
二 解决问题
这时候通过网上查询,基本可以得到以下排错手段
1、form 表单中未写 name 属性;
2、jsp 的提交方式与 servlet 不一致(如:在 jsp 中用的 post ,但是在对应 servlet 中写的是 doGet);
3、form 表单的 enctype 属性与 servlet 不一致;
4、servlet 中的 getParameter() 参数与 form 表单不一致。
但遗憾的是,我都正确配置了这些参数,可取出的就是 null 。
最后发现
在前端用了 multipart/form-data 封装 form 表单数据,后端接受到的数据是一个含文本、二进制数据的复杂数据对象,这时候肯定不能直接通过 getParameter() 获取。因为 getParameter() 方法是不能对这种 “打包数据对象” 进行解析的。这时候最简单的解决办法就是在 servlet 处添加 @MultipartConfig 注解,以告知 servlet,此时接受到的数据是同时含文本、二进制数据的,需要在 getParameter() 前进行适当预处理。这样一来, getParameter() 才能从前端发来的数据对象中正确解析出各项 name 对应的值。
所以,正确的后端代码应改为:
package com.jjy.web.servlet;
import com.jjy.pojo.FileInfo;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@MultipartConfig
@WebServlet("/file.do")
public class FileInfoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String flag = req.getParameter("flag");
System.out.println("未进入"+flag);
if ("insert".equals(flag)) {
System.out.println(flag);
insertFileinfo(req, resp);
}
}
private void insertFileinfo(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String contractName = request.getParameter("contractName");
String contractCategory = request.getParameter("contractCategory");
FileInfo fileInfo = new FileInfo();
Part part = request.getPart("contractFileUpload");
//通过Part对象得到上传的文件名
String fileName = part.getSubmittedFileName();
System.out.println("上传文件名:" + fileName);
//得到文件存放的路径
String filePath = request.getServletContext().getRealPath("/");
System.out.println("文件存放路径:" + filePath);
part.write(filePath + "/" + fileName);
fileInfo.setFilename(contractName);
fileInfo.setFiletype(contractCategory);
LocalDateTime currentDateTime = LocalDateTime.now();
// 定义日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 将LocalDateTime转换为String
String formattedDateTime = currentDateTime.format(formatter);
fileInfo.setFiletime(formattedDateTime);
System.out.println(fileInfo);
// 处理完毕后重定向或者显示成功消息
response.sendRedirect("guidang.html");
}
}
即:添加 @MultipartConfig 注解。
这时,所有的数据均能正确地取出!
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力