1. 实验要求
RestfulCrud:满足Restful风格
-
URL:
/资源名称/资源标识
HTTP请求方式区分对资源CRUD操作. 普通CRUD RestfulCRUD 查询 getEmp emp-GET 添加 addEmp?xxx emp-post 修改 updateEmp?id=xxx&xxx=xxx emp/(id)-PUT 删除 deleteEmp?id=xxx emp/(id)-DELETE -
实验要求架构:
. 请求url 请求方式 查询所有员工 emps GET 查询某个员工(来到修改页面) emp/1 GET 来到添加页面 emp GET 添加员工 emp POST 来到修改页面(查出员工进行信息回显) emp/1 GET 修改页面 emp PUT 删除员工 emp/1 DELETE
2. thymeleaf公告页面元素抽取(参考thymeleaf文档8.1)
- 参考文档
<!--1.抽取公告片段--> <div th:fragment="copy"> © 2011 The Good Thymes Virtual Grocery </div> <!--2.引入公告片段--> <div th:insert="~{footer :: copy}"></div> <!--或者--> <div th:insert="footer :: copy"></div> <!--footer为模板名--> <!--copy为抽取公共片段的名字--> <!--3.默认的效果--> <!--insert的功能默认显示在div标签中--> <!--如果使用th:insert等属性进行引入,可以不用写~{}-->
-
三种引入片段的th属性:
- th:insert:将声明的元素插入到现有的片段中
- th:replace:将声明引入的元素替换为公共片段
- th:include:将被引入的片段的内容包含进这个标签中
<footer th:fragment="copy"> © 2011 The Good Thymes Virtual Grocery </footer> <body> ... <div th:insert="footer :: copy"></div> <div th:replace="footer :: copy"></div> <div th:include="footer :: copy"></div> </body> <body> ... <div> <footer> © 2011 The Good Thymes Virtual Grocery </footer> </div> <footer> © 2011 The Good Thymes Virtual Grocery </footer> <div> © 2011 The Good Thymes Virtual Grocery </div> </body>
-
抽取主页面的顶部和侧边栏
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar"> <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
-
把抽取的元素引入到其他的文件中
<!--引入抽取的顶栏topbar--> <div th:replace="~{dashboard :: topbar}"></div> <!--引入抽取的侧边栏sidebar--> <div th:replace="~{dashboard :: sidebar}"></div>
- 但是其实实际开发过程中我们把所有的公共的部分全部抽取出来放进一个文件里面,比如说顶部栏,侧边栏,下边栏等等,我们把这些片段全部放在一个文件夹里面,然后我们所有的页面如果有用到这些东西,全部可以直接引用这个公共的页面中的片段
3. 员工列表链接高亮(参考thymeleaf文档8.2)
-
在跳转链接处声明一个参数,如果跳转的url是
mian.html
或者是emps
就显示高亮<a class="nav-link active" th:class="${activeUrl == 'main.html' ? 'nav-link active' : 'nav-link'}" href="#" th:href="@{/main.html}"> <a class="nav-link active" th:class="${activeUrl == 'emps' ? 'nav-link active' : 'nav-link'}" href="#" th:href="@{/emps}">
-
在引入这些片段的时候也要声明
<div th:replace="~{commons/bar :: sidebar(activeUrl='main.html')}"></div> <div th:replace="~{commons/bar :: sidebar(activeUrl='emps')}"></div>
4. 员工添加页面&&添加员工
-
员工添加按钮跳转
<!--发送/emp请求--> <h2><a href="emp" th:href="@{/emp}" class="btn btn-sm btn-success">员工添加</a></h2>
-
编写
Controller
类里面的toAddPage()
方法troller public class EmployeeController { @Autowired EmployeeDao employeeDao; @Autowired DepartmentDao departmentDao; /** * 来到员工添加页面 * @return */ @GetMapping("/emp") public String toAddPage(Model model){ Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("depts",departments); return "emp/add"; } }
-
编写添加页面
add.html
,主要是写一个form
表单<form> <div class="form-group"> <label>LastName</label> <input type="text" class="form-control" placeholder="jinggengchen"> </div> <div class="form-group"> <label>Email</label> <input type="email" class="form-control" placeholder="mutong0410@163.com"> </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 >Deparentment</label> <select class="form-control"> <option th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option> </select> </div> <div class="form-group"> <label>Birth</label> <input type="text" class="form-control"> </div> </form>
-
表单提交,显示添加结果
<form th:action="@{/emp}" method="post">
-
编写
controller
类/** * 添加员工 * @return */ @PostMapping("/emp") public String addEmp(Employee employee){ employeeDao.save(employee); //来到员工列表页面 //redirect:标识重定向到一个地址 return "redirect:/emps"; }
5. 修改员工信息
-
编写编辑按钮跳转链接
<a th:href="@{/emp/}+${emp.id}" class="btn btn-sm btn-primary">编辑</a>
-
编写跳转
Controller
/** * 来到编辑员工页面,查出当前员工,在页面进行回显 * @return */ @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); return "emp/edit"; }
-
返回到一个编辑页面,这个编辑页面主要是一个
form
表单,要注意修改员工的请求方式应该为put
<form th:action="@{/emp}" th:method="put">
-
编写
controller
/** * 修改员工信息 * @return */ //员工修改;需要提交员工id; @PutMapping("/emp") public String updateEmployee(Employee employee){ System.out.println("修改的员工数据:"+employee); employeeDao.save(employee); return "redirect:/emps"; }
6. 删除员工信息
-
在
list.html
页面编写删除跳转的按钮,把get
请求转换为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>
- 编写
controller
/** * 删除员工信息 */ @DeleteMapping("/emp/{id}") public String deleteEmployee(@PathVariable("id") Integer id){ employeeDao.delete(id); return "redirect:/emps"; }
7. 错误处理机制
Springboot有一个默认的错误处理页面
可以参照
ErrorMvcAutoConfiguration
类,错误处理的自动配置自动装载了以下的配置:
DefaultErrorAttributes
,BasicErrorController
,ErrorPageCustomizer
-
一旦系统出现4xx或者5xx之类的错误,
ErrorPageCustomizer
配置就会生效(定制错误的相应规则),就会来到/error
请求@Value("${error.path:/error}") private String path = "/error"; //系统出现错误以后找到error请求进行处理
-
这个请求就会被
BasicErrorController
处理//处理默认的/error请求 @Controller @RequestMapping({"${server.error.path:${error.path:/error}}"}) public class BasicErrorController extends AbstractErrorController { @RequestMapping(produces = {"text/html"})//产生html数据 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = this.getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); //去哪个页面作为错误页面,包含页面地址和页面内容 ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView("error", model); } @RequestMapping//产生json数据 public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { HttpStatus status = this.getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity(status); } else { Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL)); return new ResponseEntity(body, status); } } }
-
resolveErrorView
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) { Iterator var5 = this.errorViewResolvers.iterator(); ModelAndView modelAndView; do { if (!var5.hasNext()) { return null; } ErrorViewResolver resolver = (ErrorViewResolver)var5.next(); modelAndView = resolver.resolveErrorView(request, status, model); } while(modelAndView == null); return modelAndView; }
DefaultErrorViewResolver
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
//默认SpringBoot会去找一个页面 error/404 or error/500
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
}
- 所以如果我们定制自己的错误页面,直接在
templates
目录下创建error
文件夹,里面放404.html
文件即可