表单开发
- [Java Web 表单开发详解](#Java Web 表单开发详解)
-
Java Web 表单开发详解
一、表单开发核心组件
| 组件 |
作用 |
常用实现 |
| HTML表单 |
前端数据收集界面 |
<form>, <input>, <select>等 |
| Servlet |
接收并处理表单数据 |
HttpServlet, doPost(), doGet() |
| JSP页面 |
显示表单和处理结果 |
EL表达式, JSTL标签 |
| JavaBean |
封装表单数据 |
POJO类, 遵循JavaBean规范 |
| 验证框架 |
数据验证 |
Hibernate Validator, 自定义验证 |
二、表单数据流转流程
复制代码
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ HTML表单 │ → │ Servlet │ → │ JavaBean │ → │ 数据库 │
│ (JSP) │ ← │ (控制器) │ ← │ (模型) │ ← │ (持久层) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
↑ ↑ ↑
用户输入 业务逻辑 数据封装
三、基础表单示例
1. HTML/JSP 表单页面
jsp
复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<form action="register" method="post">
<table border="1">
<tr>
<td>用户名:</td>
<td><input type="text" name="username" required></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password" required></td>
</tr>
<tr>
<td>邮箱:</td>
<td><input type="email" name="email"></td>
</tr>
<tr>
<td>性别:</td>
<td>
<input type="radio" name="gender" value="male">男
<input type="radio" name="gender" value="female">女
</td>
</tr>
<tr>
<td>爱好:</td>
<td>
<input type="checkbox" name="hobbies" value="reading">阅读
<input type="checkbox" name="hobbies" value="sports">运动
<input type="checkbox" name="hobbies" value="music">音乐
</td>
</tr>
<tr>
<td>城市:</td>
<td>
<select name="city">
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
</select>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="注册">
<input type="reset" value="重置">
</td>
</tr>
</table>
</form>
2. JavaBean (POJO)
java
复制代码
public class User {
private String username;
private String password;
private String email;
private String gender;
private String[] hobbies;
private String city;
// 构造方法、getter和setter
public User() {}
// 必须有无参构造方法
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
// 其他getter/setter...
}
3. Servlet 处理表单
java
复制代码
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 设置编码
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 2. 获取表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
String gender = request.getParameter("gender");
String[] hobbies = request.getParameterValues("hobbies"); // 多值参数
String city = request.getParameter("city");
// 3. 数据验证
List<String> errors = new ArrayList<>();
if (username == null || username.trim().isEmpty()) {
errors.add("用户名不能为空");
}
if (password == null || password.length() < 6) {
errors.add("密码长度至少6位");
}
// 4. 封装到JavaBean
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setEmail(email);
user.setGender(gender);
user.setHobbies(hobbies);
user.setCity(city);
// 5. 业务处理和数据存储
// 6. 转发到结果页面
request.setAttribute("user", user);
request.getRequestDispatcher("/result.jsp").forward(request, response);
}
}
四、表单数据获取方式对比
| 方法 |
说明 |
适用场景 |
request.getParameter("name") |
获取单个参数值 |
文本框、单选框、下拉单选 |
request.getParameterValues("name") |
获取多个参数值 |
复选框、多选列表 |
request.getParameterMap() |
获取所有参数Map |
批量处理参数 |
| BeanUtils.populate() |
自动封装到JavaBean |
表单字段与JavaBean属性对应 |
| @ModelAttribute (Spring MVC) |
自动绑定参数 |
Spring框架中使用 |
五、表单验证实现
1. 客户端验证 (JavaScript)
javascript
复制代码
function validateForm() {
var username = document.forms["myForm"]["username"].value;
if (username == "") {
alert("用户名必须填写");
return false;
}
return true;
}
2. 服务器端验证 (Java)
java
复制代码
public class FormValidator {
public static Map<String, String> validate(User user) {
Map<String, String> errors = new HashMap<>();
// 用户名验证
if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {
errors.put("username", "用户名不能为空");
} else if (user.getUsername().length() < 3 || user.getUsername().length() > 20) {
errors.put("username", "用户名长度应在3-20字符之间");
}
// 邮箱验证
if (user.getEmail() != null && !user.getEmail().isEmpty()) {
String emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$";
if (!user.getEmail().matches(emailRegex)) {
errors.put("email", "邮箱格式不正确");
}
}
return errors;
}
}
3. 使用Hibernate Validator
java
复制代码
public class User {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度3-20字符")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码至少6位")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
// getter/setter...
}
六、文件上传表单
1. 表单设置
html
复制代码
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="上传">
</form>
2. 使用Apache Commons FileUpload
java
复制代码
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 检查是否为multipart表单
if (!ServletFileUpload.isMultipartContent(request)) {
response.getWriter().print("错误:表单必须包含enctype=multipart/form-data");
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
if (!item.isFormField()) { // 文件字段
String fileName = new File(item.getName()).getName();
String filePath = getServletContext().getRealPath("/uploads") + File.separator + fileName;
File storeFile = new File(filePath);
item.write(storeFile);
}
}
response.getWriter().print("文件上传成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}
七、表单开发最佳实践
| 实践要点 |
说明 |
| 编码统一 |
始终使用UTF-8编码,避免乱码问题 |
| 双重验证 |
客户端验证提升体验,服务器端验证确保安全 |
| 防CSRF攻击 |
使用令牌(token)机制防止跨站请求伪造 |
| 防止SQL注入 |
使用PreparedStatement,避免拼接SQL |
| XSS防护 |
对用户输入进行过滤或转义 |
| 数据持久化 |
使用DAO模式分离数据访问逻辑 |
| 错误处理 |
友好的错误提示,避免暴露系统信息 |
八、MVC模式下的表单处理
java
复制代码
// Controller (Servlet)
@WebServlet("/user")
public class UserController extends HttpServlet {
private UserService userService = new UserService();
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取参数并封装
User user = new User();
BeanUtils.populate(user, request.getParameterMap());
// 2. 验证
Map<String, String> errors = Validator.validate(user);
if (!errors.isEmpty()) {
request.setAttribute("errors", errors);
request.getRequestDispatcher("/register.jsp").forward(request, response);
return;
}
// 3. 调用Service
boolean success = userService.register(user);
// 4. 返回结果
if (success) {
response.sendRedirect("success.jsp");
} else {
request.setAttribute("error", "注册失败,用户名可能已存在");
request.getRequestDispatcher("/register.jsp").forward(request, response);
}
}
}
九、现代框架中的表单处理
Spring MVC 示例
java
复制代码
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/register")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String submitForm(@Valid @ModelAttribute("user") User user,
BindingResult result,
Model model) {
if (result.hasErrors()) {
return "register"; // 返回表单页面显示错误
}
userService.save(user);
return "redirect:/success";
}
}
十、常见问题解决方案
| 问题 |
原因 |
解决方案 |
| 中文乱码 |
编码不一致 |
request.setCharacterEncoding("UTF-8") |
| 获取不到参数 |
表单enctype设置错误 |
普通表单不要用multipart/form-data |
| 复选框获取单个值 |
使用getParameter而不是getParameterValues |
区分单选和多选场景 |
| 文件上传失败 |
文件大小限制 |
配置最大文件大小限制 |
| 重复提交 |
用户刷新或回退 |
使用PRG模式(Post-Redirect-Get) |