Spring-Boot的Web开发(二)

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">      
        &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">  &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>
    
  • 抽取主页面的顶部和侧边栏
    <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文件即可
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容