1. 實驗要求
RestfulCrud:滿足Restful風格
-
URL:
/資源名稱/資源標識
HTTP請求方式區(qū)分對資源CRUD操作. 普通CRUD RestfulCRUD 查詢 getEmp emp-GET 添加 addEmp?xxx emp-post 修改 updateEmp?id=xxx&xxx=xxx emp/(id)-PUT 刪除 deleteEmp?id=xxx emp/(id)-DELETE -
實驗要求架構(gòu):
. 請求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:將聲明的元素插入到現(xiàn)有的片段中
- th:replace:將聲明引入的元素替換為公共片段
- th:include:將被引入的片段的內(nèi)容包含進這個標簽中
<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>
-
抽取主頁面的頂部和側(cè)邊欄
<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> <!--引入抽取的側(cè)邊欄sidebar--> <div th:replace="~{dashboard :: sidebar}"></div>
- 但是其實實際開發(fā)過程中我們把所有的公共的部分全部抽取出來放進一個文件里面,比如說頂部欄,側(cè)邊欄,下邊欄等等,我們把這些片段全部放在一個文件夾里面,然后我們所有的頁面如果有用到這些東西,全部可以直接引用這個公共的頁面中的片段
3. 員工列表鏈接高亮(參考thymeleaf文檔8.2)
-
在跳轉(zhuǎn)鏈接處聲明一個參數(shù),如果跳轉(zhuǎn)的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. 員工添加頁面&&添加員工
-
員工添加按鈕跳轉(zhuǎn)
<!--發(fā)送/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>
-
表單提交,顯示添加結(jié)果
<form th:action="@{/emp}" method="post">
-
編寫
controller
類/** * 添加員工 * @return */ @PostMapping("/emp") public String addEmp(Employee employee){ employeeDao.save(employee); //來到員工列表頁面 //redirect:標識重定向到一個地址 return "redirect:/emps"; }
5. 修改員工信息
-
編寫編輯按鈕跳轉(zhuǎn)鏈接
<a th:href="@{/emp/}+${emp.id}" class="btn btn-sm btn-primary">編輯</a>
-
編寫跳轉(zhuǎn)
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
表單,要注意修改員工的請求方式應(yīng)該為put
<form th:action="@{/emp}" th:method="put">
-
編寫
controller
/** * 修改員工信息 * @return */ //員工修改闯第;需要提交員工id烤惊; @PutMapping("/emp") public String updateEmployee(Employee employee){ System.out.println("修改的員工數(shù)據(jù):"+employee); employeeDao.save(employee); return "redirect:/emps"; }
6. 刪除員工信息
-
在
list.html
頁面編寫刪除跳轉(zhuǎn)的按鈕,把get
請求轉(zhuǎn)換為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
-
一旦系統(tǒng)出現(xiàn)4xx或者5xx之類的錯誤,
ErrorPageCustomizer
配置就會生效(定制錯誤的相應(yīng)規(guī)則),就會來到/error
請求@Value("${error.path:/error}") private String path = "/error"; //系統(tǒng)出現(xiàn)錯誤以后找到error請求進行處理
-
這個請求就會被
BasicErrorController
處理//處理默認的/error請求 @Controller @RequestMapping({"${server.error.path:${error.path:/error}}"}) public class BasicErrorController extends AbstractErrorController { @RequestMapping(produces = {"text/html"})//產(chǎn)生html數(shù)據(jù) 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()); //去哪個頁面作為錯誤頁面,包含頁面地址和頁面內(nèi)容 ModelAndView modelAndView = this.resolveErrorView(request, response, status, model); return modelAndView != null ? modelAndView : new ModelAndView("error", model); } @RequestMapping//產(chǎn)生json數(shù)據(jù) 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
目錄下創(chuàng)建error
文件夾,里面放404.html
文件即可