CRUD(27):新增-JSR303后端校验
重要的数据都应该加上后端校验,防止用户绕过前端校验提交非法数据。
一、新增逻辑
①在index.jsp页面点击新增;
②弹出新增对话框;
③去数据库查询部门列表,显示在对话框中;
④用户输入数据,并进行校验:
首先输入用户名,这里的输入框绑定了ajax校验。所以输入完成以后,会发送ajax请求校验用户名是否格式正确,是否在数据库中重复。
点击保存后,保存按钮。如果ajax用户名校验失败,返回错误;如果前端校验失败,返回错误。最后发送ajax请求进行jsr303后端校验,校验成功则保存员工,校验失败则返回错误信息。
jquery前端校验,ajax用户名重复校验,重要数据(后端)JSR303校验,唯一约束;
⑤完成保存。
二、要支持JSR303校验,首先导入hiernate validator。
<!--
https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
这个我也之前导入过了。
二、使用JSR303校验,在每一个要校验的字段增加上对应的规则。
在Employee.java中添加:
@Pattern(regexp = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5}$)",
message = "jsr303:用户名可以是2-5位中文、6-16位字母、横杠或下划线")
private String empName;
@Pattern(regexp =
"^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})",message = "jsr303:请输入正确的邮箱格式,如abc@qq.com")
private String email;
三、在员工保存的方法中添加这个校验。
// 保存员工
@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(@Valid Employee employee, BindingResult result) {
if (result.hasErrors()) {
//显示校验失败的错误信息
Map<String,Object> map = new HashMap();
List<FieldError> errors = result.getFieldErrors();
for(FieldError fieldError:errors) {
System.out.println("错误的字段:"+fieldError.getField());
System.out.println("错误信息:"+fieldError.getDefaultMessage());
map.put(fieldError.getField(),fieldError.getDefaultMessage());
}
//校验失败返回失败
return Msg.fail();
} else {
employeeService.saveEmp(employee);
return Msg.success();
}
}
四、改写ajax请求
//2.发送ajax请求保存员工;
$.ajax({
url : "${APP_PATH}/emp",
type : "POST",
data : $("#empAddModal form").serialize(),
success : function(result) {
///alert(result.msg);
if (result.code == 100) {
//关闭模态框
$("#empAddModal").modal('hide');
//来到最后一页显示刚才保存的数据
//发送ajax请求显示最后一页数据
to_page(totalRecord);
} else {
//显示失败信息
console.log(result);
}
}
});
五、测试一下就发现了一个问题。
已知用户名小杨是存在的:

然后再输入用户名小杨:

ajax校验请求返回了用户名重复的失败信息,这里没问题。
然后我们不管它,输入一个错误的邮箱地址,点一下保存:

这里就不显示ajax校验了,全部显示jquery前端校验的结果。
然后我们把邮箱地址改成正确的,再点一下保存:

现在jquery完全校验正确了。
但是,我们F12看一下:

并没有发送保存数据的请求。
再看下代码:
ajax-va="error",所以没保存。
那我们就把这个error改成success,再点一下保存:
对话框消失了!记录也跳到了最后一页,但是没有显示我们新保存的小杨的个人信息。
F12看一下network:

可以看到这个发送的ajax请求返回的状态是成功,并且我们还发送了pn=1021
但是!!!数据库里面查询到了!!

六、问题的解决
最开始那个问题很好解决,把用户名校验放在前面,前端校验放在后面就好了,这样就不会出现邮箱出现错误以后,用户名反而校验成功的问题。
至于把error改成success提交,绕过验证的问题——
对不起我也不知道怎么办了,只好在数据库中对用户名添加了一个唯一性约束,这时候保存还是能点保存,但数据无法写入。

七、源代码
新增功能到这里就差不多了,虽然不知道以后实际运用还会出什么千奇百怪的问题。
这里放一下index.jsp的源代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>员工列表</title>
<!-- 拿到当前项目路径,以/开始,不以/结束 -->
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<!-- 引入jquery -->
<script type="text/javascript"
src="${APP_PATH }/static/js/jquery-1.12.4.min.js"></script>
<!-- 引入Bootstrap样式 -->
<link
href="${APP_PATH }/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"
rel="stylesheet">
<script
src="${APP_PATH
}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
<!-- 新增的modal -->
<div class="modal fade" id="empAddModal" tabindex="-1" role="dialog"
aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close"
data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">新增员工</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label for="empName_add_input"
class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<input type="text"
class="form-control" name="empName"
id="empName_add_input" placeholder="empName">
<span
class="help-block">
</span>
</div>
</div>
<div class="form-group">
<label for="email_add_input"
class="col-sm-2 control-label">email</label>
<div class="col-sm-10">
<input type="text"
class="form-control" name="email"
id="email_add_input"
placeholder="hello123@qq.com"> <span
class="help-block">
</span>
</div>
</div>
<div class="form-group">
<label for="gender_add_input"
class="col-sm-2 control-label">性别</label>
<div class="col-sm-10">
<label
class="radio-inline"> <input type="radio"
name="gender"
id="gender1_add_input" value="m"
checked="checked">
男
</label> <label
class="radio-inline"> <input type="radio"
name="gender"
id="gender1_add_input" value="f"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2
control-label">deptName</label>
<div class="col-sm-4">
<!-- 部门提交部门ID即可 -->
<select
class="form-control" name="dId" id="dept_add_select">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary"
id="emp_save_btn">保存</button>
</div>
</div>
</div>
</div>
<!-- 搭建显示页面 -->
<div class="container">
<!-- 标题 -->
<div class="row">
<div class="col-md-12">
<h1>SSM-CRUD</h1>
</div>
</div>
<!-- 按钮 -->
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary"
id="emp_add_modal_btn">新增</button>
<button class="btn btn-danger">删除</button>
</div>
</div>
<!-- 显示表格数据 -->
<div class="row">
<div class="col-md-12">
<table class="table table-hover" id="emps_table">
<thead>
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<!-- 显示分页 -->
<div class="row">
<!-- 分页文字信息 -->
<div class="col-md-6" id="page_info_area"></div>
<!-- 分页条信息 -->
<div class="col-md-6" id="page_nav_area"></div>
</div>
</div>
<script type="text/javascript">
<!--页面加载完成以后直接发送一个ajax请求,拿到分页数据-->
var totalRescord;
$(function() {
to_page(1);
});
function to_page(pn) {
$.ajax({
url : "${APP_PATH}/emps",
data : "pn=" + pn,
type : "GET",
success : function(result) {
//console.log(result);
//1.解析并显示员工数据
build_emps_table(result);
//2.解析并显示分页信息
build_page_info(result);
//3.解析并显示分页条
build_page_nav(result);
}
});
}
//解析显示员工数据
function build_emps_table(result) {
//首先清空原来的emps_table tbody
$("#emps_table tbody").empty();
var emps = result.extend.pageInfo.list;
$.each(emps, function(index, item) {
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName);
var genderTd = $("<td></td>").append(
item.gender == 'm' ? "男" : "女");
var emailTd = $("<td></td>").append(item.email);
var deptNameTd = $("<td></td>")
.append(item.department.deptName);
var editBtn = $("<button></button>").addClass(
"btn btn-info btn-sm").append(
$("<span></span>").addClass(
"glyphicon
glyphicon-pencil")).append("编辑");
var delBtn = $("<button></button>").addClass(
"btn btn-danger btn-sm").append(
$("<span></span>")
.addClass("glyphicon
glyphicon-trash")).append(
"删除");
var btnTd = $("<td></td>").append(editBtn).append("
").append(
delBtn);
//append方法执行完成以后还是返回原来的元素
$("<tr></tr>").append(empIdTd).append(empNameTd).append(
genderTd).append(emailTd).append(deptNameTd).append(
btnTd).appendTo("#emps_table tbody");
});
}
//显示分页信息
function build_page_info(result) {
//首先清空原来的
$("#page_info_area").empty();
$("#page_info_area").append(
"当前第" + result.extend.pageInfo.pageNum + "页
共"
+ result.extend.pageInfo.pages +
"页 共"
+ result.extend.pageInfo.total +
"条记录");
totalRecord = result.extend.pageInfo.total;
}
//显示分页条
function build_page_nav(result) {
//首先清空原来的
$("#page_nav_area").empty();
var ul = $("<ul></ul>").addClass("pagination");
var firstPageLi =
$("<li></li>").append($("<a></a>").append("首页"))
.attr("href", "#");
var prePageLi = $("<li></li>").append(
$("<a></a>").append("«"));
if (result.extend.pageInfo.hasPreviousPage == false) {
prePageLi.addClass("disabled");
} else {
prePageLi.click(function() {
to_page(result.extend.pageInfo.pageNum - 1);
});
}
var nextPageLi = $("<li></li>").append(
$("<a></a>").append("»"));
if (result.extend.pageInfo.hasNextPage == false) {
nextPageLi.addClass("disabled");
} else {
nextPageLi.click(function() {
to_page(result.extend.pageInfo.pageNum + 1);
});
}
var lastPageLi =
$("<li></li>").append($("<a></a>").append("末页"))
.attr("href", "#");
firstPageLi.click(function() {
to_page("1");
});
lastPageLi.click(function() {
to_page(result.extend.pageInfo.pages);
});
//添加首页和前一页的显示
ul.append(firstPageLi).append(prePageLi);
$.each(result.extend.pageInfo.navigatepageNums,
function(index,
item) {
var numLi =
$("<li></li>").append($("<a></a>").append(item));
//页码按钮选中变蓝
if (result.extend.pageInfo.pageNum == item) {
numLi.addClass("active");
}
numLi.click(function() {
to_page(item);
});
//添加页码显示
ul.append(numLi);
});
//添加下一页和末页的显示
ul.append(nextPageLi).append(lastPageLi);
var navEle = $("<nav></nav>").append(ul);
navEle.appendTo("#page_nav_area");
}
//点击新增按钮
$("#emp_add_modal_btn").click(function() {
//表单重置
reset_form("#empAddModal form");
//发送ajax请求,显示部门信息,出现在下拉列表中
getDepts();
//弹出模态框
$("#empAddModal").modal({
backdrop : "static"
});
});
function getDepts() {
$.ajax({
url : "${APP_PATH}/depts",
type : "GET",
success : function(result) {
//显示部门信息在下拉列表中
$.each(result.extend.depts, function() {
var optionEle =
$("<option></option>").append(
this.deptName).attr("value", this.deptId);
optionEle.appendTo("#dept_add_select");
});
}
});
}
//定义校验函数
//校验名字
function validate_add_form() {
//1.拿到校验数据,使用正则表达式进行校验
var empName = $("#empName_add_input").val();
var regName =
/(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
var email = $("#email_add_input").val();
var regEmail =
/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
//校验名字
if (!regName.test(empName)) {
show_validate_msg("#empName_add_input", "error",
"jquery:用户名可以是2-5位中文、6-16位字母、横杠或下划线");
return false;
} else {
show_validate_msg("#empName_add_input", "success",
"jquey:用户名格式正确!");
}
//校验邮箱
if (!regEmail.test(email)) {
show_validate_msg("#email_add_input", "error",
"jquery:请输入正确的邮箱格式,如abc@qq.com");
return false;
} else {
show_validate_msg("#email_add_input", "success",
"jquery:邮箱格式正确!");
}
return true;
}
function reset_form(ele) {
//清除表单数据
$(ele)[0].reset();
//清空表单样式
$(ele).find("*").removeClass("has-success has-error");
$(ele).find(".help-block").text("");
}
function show_validate_msg(ele, status, msg) {
//清除校验状态
$(ele).parent().removeClass("has-success has-error");
if ("success" == status) {
$(ele).parent().addClass("has-success");
$(ele).next().text(msg);
} else if ("error" == status) {
$(ele).parent().addClass("has-error");
$(ele).next().text(msg);
}
}
$("#empName_add_input").change(
function() {
//发送ajax请求校验用户名是否可用
var empName = this.value;
$.ajax({
url : "${APP_PATH}/checkuser",
data : "empName=" + empName,
type : "POST",
success : function(result) {
if (result.code == 100) {
show_validate_msg("#empName_add_input",
"success",
"ajax:用户名不重复,可用√");
$("#emp_save_btn").attr("ajax-va", "success");
} else {
show_validate_msg("#empName_add_input",
"error",
result.extend.va_msg);
$("#emp_save_btn").attr("ajax-va", "error");
}
}
});
});
//点击保存按钮
$("#emp_save_btn").click(function() {
//1.模态框中的数据提交给服务器进行保存;
//判断用户名校验是否成功
if ($(this).attr("ajax-va") == "error") {
return false;
}
;
//对要提交给服务器的数据进行前端校验
if (!validate_add_form()) {
return false;
}
;
//2.发送ajax请求保存员工;
$.ajax({
url : "${APP_PATH}/emp",
type : "POST",
data : $("#empAddModal form").serialize(),
success : function(result) {
///alert(result.msg);
if (result.code == 100) {
//关闭模态框
$("#empAddModal").modal('hide');
//来到最后一页显示刚才保存的数据
//发送ajax请求显示最后一页数据
to_page(totalRecord);
} else {
//显示失败信息
if (undefined !=
result.extend.errorFields.email) {
alert(result.extend.errorFields.email);
}
if (undefined !=
result.extend.errorFields.empName) {
alert(result.extend.errorFields.empName);
}
}
}
});
});
</script>
</body>
</html>
浙公网安备 33010602011771号