总目标:开发一个spingboot的web项目,项目地址:点我
今天的主要任务是完成员工列表的增删改功能。
1. 增加
-
在list页面,对增加按钮添加映射路径
<a class="btn btn-sm btn-success" th:href="@{/emp}">员工添加</a>
-
在templates/emp文件夹下添加add.html
add页面与list页面相比,topbar和sidebar不变,只不过主体内容多了一个form表单
<form> <div class="form‐group"> <label>LastName</label> <input type="text" class="form‐control" placeholder="zhangsan"> </div> <div class="form‐group"> <label>Email</label> <input type="email" class="form‐control" placeholder="[email protected]"> </div> <div class="form‐group"> <label>Gender</label><br/> <div class="form‐check form‐check‐inline"> <input class="form‐check‐input" type="radio" name="gender" value="1"> <label class="form‐check‐label">男</label> </div> <div class="form‐check form‐check‐inline"> <input class="form‐check‐input" type="radio" name="gender" value="0"> <label class="form‐check‐label">女</label> </div> </div> <div class="form‐group"> <label>department</label> <select class="form‐control"> <option>1</option> <option>2</option> <option>3</option> <option>4</option> <option>5</option> </select> </div> <div class="form‐group"> <label>Birth</label> <input type="text" class="form‐control" placeholder="zhangsan"> </div> <button type="submit" class="btn btn‐primary">添加</button> </form>
-
EmployeeController添加映射
@Autowired DepartmentDao departmentDao; //来到员工添加页面 @GetMapping("/emp") public String toAddPage(Model model){ Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("depts", departments); return "emp/add"; }
-
添加页面填写的部门信息,是根据请求域中的depts来填充的,修改add.html
<div class="form‐group"> <label>department</label> <select class="form‐control"> <option th:id="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option> </select> </div>
-
在添加页面,修改form表单映射路径,根据web实战1中的框架设计,添加员工的请求路径为
/emp
,请求方式为post<form th:action="@{/emp}" method="post">
还要注意form表单中提交的每一个数据都要加上name属性,属性值要和实体类Employee对应
-
在EmployeeeController处理请求
//员工添加 //springmvc自动关机将请求参数与入参对象的属性进行一一绑定, //前提是请求参数的名字与javaBean的属性值要一致。 @PostMapping("/emp") public String addEmp(Employee employee){ System.out.println("保存的员工信息"+employee); employeeDao.save(employee); //如果返回员工列表用return "emps",将自动拼接为classpath:/templates/emps.html return "redirect:/emps"; }
-
日期提交格式默认是按照
yyyy/MM/dd
,其他格式就会出现400错误。如果想使用yyyy-MM-dd
的格式,需要在application.properties文件中添加spring.mvc.date-format=yyyy-MM-dd
这时就能用
yyyy-MM-dd
而不能使用yyyy/MM/dd
的格式。
2. 修改
-
根据本实验的请求架构
实验功能 请求URI 请求方式 来到修改页面 emp/{id} GET 修改员工 emp PUT 点击list页面中的编辑按钮,会发送
emp/{id}
请求,在list.html中使用连接字符串的方式构成请求路径。我直接在add.html中做修改,使add.html同时能够完成添加员工和修改员工的功能。
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a>
-
在EmployeeController中处理请求
//来到修改页面,查出当前员工以及部门信息,在页面回显
@GetMapping("/emp/{id}")
public String toEditPage(@PathVariable("id") Integer id, Model model){
Employee employee = employeeDao.get(id);
model.addAttribute("emp", employee);
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts", departments);
//回到修改页面(使add.html同时完成添加和修改员工的功能)
return "emp/add";
}
-
修改add.html,为每个input标签添加value值,当点击编辑按钮的时候,能够回显当前员工的值
-
但是经过上一步的修改,add.html页面的添加功能被扰乱了。修改的时候请求域中有employee和departmeng对象,而添加的时候请求域中只有department对象。当点击添加按钮,employee为空,所以添加页面很多元素都不显示。我们可以根据emp是否为空来判断当前add.html是在处理修改还是在添加员工
当点击编辑按钮,进入编辑页面,页面底部的按钮要根据当前add.html正在完成的功能显示为修改
或添加
<button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'"></button>
-
接下来,点击修改页面底部的修改按钮要发送put请求,但是不能直接将表单的请求方法直接改为
put
。在springmvc中有三步:
1)SpringMVC中配置HiddenHttpMethodFilter,其将请求转换为指定的方式 (在springboot已自动配置好)
2)页面创建一个post表单(当前form即为post请求)
3)创建一个input项,name="_method";value的属性值就是我们指定的请求方式
在springboot只需要进行后两步,我们添加一个隐藏的input标签,指定value的值为put,并且当emp不为空(即当前处理的是修改请求)
<form th:action="@{/emp}" method="post"> <input type="hidden" name="_method" value="put" th:if="${emp!=null}"/>
-
点击修改按钮后,上一步已将请求方式改为put,接下来需要在EmployeeController中添加处理该请求的方法。
@PutMapping("/emp") public String updateEmployee(Employee employee){ System.out.println("修改的员工的数据"+employee); employeeDao.save(employee); return "redirect:/emps"; }
运行程序,修改一个员工信息为例,发现控制台输出的员工信息缺少id,且员工信息没有修改,而是多加了一个员工,将刚才的修改信息添加到一个新员工中去了
是因为调用EmployeeDao中的save()方法时,如果没有员工id,会自增长id,并添加新员工private static Integer initId = 1006; public void save(Employee employee){ if(employee.getId() == null){ employee.setId(initId++); } employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId())); employees.put(employee.getId(), employee); }
因此,当我们修改员工信息时,需要在add.html中提交一个员工id,我们用隐藏的input标签来完成
<input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}"/>
至此,修改的功能就完成了。
如果不能提交put请求,请查看第4部分的bug处理
3. 删除
-
根据本实验的请求架构
实验功能 请求URI 请求方式 删除员工 emp/{id} DELETE 点击list页面中的删除按钮,会发送
emp/{id}
的delete方式的请求,在list.html中使用连接字符串的方式构成请求路径。我们使用隐藏的input标签来指定delete请求<form th:action="@{/emp/}+${emp.id}" method="post"> <input type="hidden" name="_method" value="delete"/> <button type="submit" class="btn btn-sm btn-danger">删除</button> </form>
-
在EmployeeController处理请求
//删除员工 @DeleteMapping("/emp/{id}") public String deleteEmployee(@PathVariable("id") Integer id){ System.out.println("id:"+id); employeeDao.delete(id); return "redirect:/emps"; }
运行程序,能够实现删除员工的功能,但是list页面中的每一个删除按钮都关联着一个form表单,显得比较笨重,于是下一步打算把form表单抽取出来,使用js的方式来发送请求。
-
使用js提交请求,这个请求地址应该是从删除按钮获取的。我们给删除按钮添加一个自定义的属性,里面放入请求路径,在js中,将这个自定义属性的值作为表单form的action值。
删除按钮相关代码为
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button>
将表单放到其他地方
<form id="deleteEmpForm" method="post"> <input type="hidden" name="_method" value="delete"/> </form>
在list.html中添加js代码
<script>
$(".deleteBtn").click(function(){
//删除当前员工的
$("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
return false;
});
</script>
然后就大功告成了。
4. bug处理
springboot自动配置了hiddenHttpMethodFilter,这个过滤器帮我们把post请求转换为put请求或delete请求。但是我自己动手实践的过程中,发现这个自动配置并没有生效。
解决方式有两种:
-
自己在配置类中添加一个过滤器
//hiddenHttpMethodFilter @Bean public FilterRegistrationBean timeFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); HiddenHttpMethodFilter myFilter = new HiddenHttpMethodFilter(); registrationBean.setFilter(myFilter); ArrayList<String> urls = new ArrayList<>(); urls.add("/*");//配置过滤规则 registrationBean.setUrlPatterns(urls); return registrationBean; }
-
在application.properties中修改默认配置
查看WebMvcAutoConfig,发现其关于HiddenHttpMethodFilter的代码如下所示:@Bean @ConditionalOnMissingBean({HiddenHttpMethodFilter.class}) @ConditionalOnProperty( prefix = "spring.mvc.hiddenmethod.filter", name = {"enabled"}, matchIfMissing = false ) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter(); }
注解
@ConditionalOnProperty
的详细属性如下所示@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { String[] value() default {}; //数组,获取对应property名称的值,与name不可同时使用 String prefix() default "";//property名称的前缀,可有可无 String[] name() default {};//数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用 String havingValue() default "";//可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置 boolean matchIfMissing() default false;//缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错 boolean relaxedNames() default true;//是否可以松散匹配,至今不知道怎么使用的 } }
matchIfMissing = false
表明在application.properties中如果缺少这个property,不会配置HiddenHttpMethodFilter(不知道我理解的是否正确)。因此组合prifix
和name
,在application.properties中添加如下代码修改默认配置即可spring.mvc.hiddenmethod.filter.enabled=true