Spring-Boot的Web開發(fā)(二)

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">      
        &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">  &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>      
            &copy; 2011 The Good Thymes Virtual Grocery    
        </footer>  
    </div>
    
    <footer>    
        &copy; 2011 The Good Thymes Virtual Grocery  
    </footer>
    
    <div>    
        &copy; 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文件即可
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朗儒,一起剝皮案震驚了整個濱河市略荡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹬叭,老刑警劉巖乡摹,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缩赛,居然都是意外死亡耙箍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門酥馍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辩昆,“玉大人,你說我怎么就攤上這事旨袒≈耄” “怎么了术辐?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長施无。 經(jīng)常有香客問我辉词,道長,這世上最難降的妖魔是什么猾骡? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任瑞躺,我火速辦了婚禮,結(jié)果婚禮上卓练,老公的妹妹穿的比我還像新娘隘蝎。我一直安慰自己,他們只是感情好襟企,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布嘱么。 她就那樣靜靜地躺著,像睡著了一般顽悼。 火紅的嫁衣襯著肌膚如雪曼振。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天蔚龙,我揣著相機與錄音冰评,去河邊找鬼。 笑死木羹,一個胖子當著我的面吹牛甲雅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坑填,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼抛人,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脐瑰?” 一聲冷哼從身側(cè)響起妖枚,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苍在,沒想到半個月后绝页,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡寂恬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年续誉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初肉。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡屈芜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情井佑,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布眠寿,位于F島的核電站躬翁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盯拱。R本人自食惡果不足惜盒发,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狡逢。 院中可真熱鬧宁舰,春花似錦、人聲如沸奢浑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雀彼。三九已至壤蚜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間徊哑,已是汗流浹背袜刷。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莺丑,地道東北人著蟹。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像梢莽,于是被迫代替她去往敵國和親萧豆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容