• SpringMVC 之 RESTful 风格的增删改查


    1. 视图和视图解析器

    1. 视图解析器
      • 请求处理方法执行完成后,最终返回一个ModelAndView对象,对于返回String,View 或 ModelMap
        等类型的处理方法, SpringMVC 也会在内部将它们装配成一个ModelAndView对象;
      • SpringMVC借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP,
        EXCEL,PDF等各种表现形式的视图;
    2. 视图
      • 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户;
      • View 接口,位于 org.springframework.web.servlet包中;
      • 视图对象由视图解析器负责实例化.由于视图是无状态的,所以它们不会有线程安全的问题;

    2. mvc:view-controller标签

    • 作用: 在不需要Controller处理request请求的情况下,直接将设置的View交给相应的视图解析器解析为
      视图;
    // 在 springDispatcherServlet-servlet.xml 中配置
    <mvc:view-controller path="/自定义网址" view-name="View视图页面名称"/>
    
    // 还需要使用 mvc:annotation-driven 标签,否则,在使用 Controller 处理request请求时,访问
    // 该页面,会报异常;
    <mvc:annotation-driven></mvc:annotation-driven>
    

    3. 自定义视图

    3.1 具体实现步骤

    • 建立专门的view包: cn.itcast.springmvc.views;
    • 编写一个View接口的实现类;
    • BeanNameViewResolver配置进springmvc配置文件;
    // 1. 创建View接口的实现类
    @Component
    public class HelloView implements View{
    
        public String getContentType(){
            return "text/html;charset=UTF-8";
        }
    
        public void render(Map<String,?> model, HttpServletRequest request,
                                    HttpServletResponse response) throws Exception{
    
            response.setContentType("text/html;charset=utf-8");
    
            response.getWriter().write("自定义视图显示");
            response.getWriter().flush();
            response.getWriter().close();                            
            }
    }
    
    // 2. springDispatcherServlet-servlet.xml 中配置视图解析器
    <bean id="BeanNameViewResolver"
                      class="org.springframework.web.servlet.view.BeanNameViewResolver">
    
        <!-- 自定义order,越小越靠前 --
        <property name="order" value="30"></property>
    </bean>
    
    // 3. demo.java
    @Controller
    public class HelloWorld{
        @RequestMapping(value="/helloworld",method=RequestMethod.GET)
        public String helloworld(){
            System.out.println("执行成功");
    
            return "helloView";
        }
    }
    

    4. 请求转发和重定向

    • 地址栏是否变化,参数能否取得,发送了几次请求;
    • 请求转发:一个请求一个响应;
    • 重定向:两个请求两个响应,两个请求之间毫无关系,所以第一个请求里面保存的信息在第二个请求里面无法获得;
    // demo.java
    @Controller
    public class Helloworld{
    
        // 重定向
        @RequestMapping(value="/redirect",method=RequestMethod.GET)
        public String helloworld(){
            System.out.println("程序运行正常");
            return "redirect:/1.jsp";
        }
    
        // 转发
        @RequestMapping(value="/forward",method=RequestMethod.GET)
        public String helloworld(){
            System.out.println("程序运行正常");
            return "forward:/2.jsp";
        }
    }
    

    5. RESTful SpringMVC CRUD

    5.1 REST(Representational State Transfer) 架构的主要原则
    • 网络上的所有事物都可被抽象为资源(Resource);
    • 每个资源都有一个唯一的资源标识符(Resource Identifier);
    • 同一资源具有多种表现形式(xml,json等);
    • 对资源的各种操作不会改变资源标识符;
    • 所有的操作都是无状态的(Stateless);
    • 符合REST原则的架构方式,即可称为RESTful;
    // 需求:
    //   1. 查询
    //        * URI: emps
    //        * 请求方式: GET
    //   2. 添加所有员工信息
    //        2.1 显示添加页面:
    //                * URI: emp
    //                * 请求方式: GET
    //        2.2 添加员工
    //                * URI: emp
    //                * 请求方式: POST
    //                * 添加完成后,重定向到 list 页面
    //   3. 删除
    //        * URI: emp/{id}
    //        * 请求方式: DELETE
    //   4. 修改操作 (其中 lastName 不可修改!!)
    //       4.1 显示修改页面
    //              * URI: emp/{id}
    //              * 请求方式: GET
    //       4.2 修改员工信息
    //              * URI: emp
    //              * 请求方式: PUT
    //              * 完成修改,重定向到 list 页面
    
    // index.jsp
    显示所有员工信息: <a href="${pageContext.request.contextPath}/emps">显示所有</a>
    
    // list.jsp (显示所有员工信息页面)
    <head>
        <!-- 引入 jquery 脚本 -->
        <script type="text/javascript"
                src="${pageContext.request.contextPath}/scripts/jquery-3.2.1.min.js">
        </script>
        <!-- 使用表单提交 delete 请求 -->
        <script type="text/javascript">
            $(function(){
                $(".deleteCss").click(function(){
                    var action = $(this).attr("href");
                    $("#deleteForm").attr("action",action).submit();
    
                    <!-- 取消 click 的默认事件 -->
                    return false;
                });
            });
        </script>
    </head>
    <body>
        <h2>显示所有员工</h2>
        <c:if test="${empty requestScope.employees}">
            it's nothing...
        </c:if>
        <c:if test="${not empty requestScope.employees}">
            <table border="1" cellpadding="10" cellspacing="2">
            <tr>
                <td>id</td>
                <td>lastName</id>
                <td>email</td>
                <td>gender</id>
                <td>department</td>
                <td>edit</id>
                <td>delete</td>
            </tr>
            <c:forEach items="${requestScope.employees}" var="employee">
                <tr>
                    <td>${employee.id}</td>
                    <td>${employee.lastName}</id>
                    <td>${employee.email}</td>
                    <td>${employee.gender == 1 ? 'male' : 'female'}</id>
                    <td>${employee.department.departmentName}</td>
                    <td>
                    <a href="${pageContext.request.contextPath}/emp/${employee.id}">
                        edit</a></id>
                    <td>
                        <a class="deleteCss"
                        href="${pageContext.request.contextPath}/emp/${employee.id}>
                            delete
                        </a>
                    </td>
                </tr>
            </c:forEach>
        </c:if>
    
        添加员工: <a href="${pageContext.request.contextPath}/emp">添加</a>
        <br/>
    
        <!-- 需要执行RESTful风格的删除,需要使用form表单 post 提交 -->
        <form id="deleteForm" action="" method="post">
            <input type="hidden" name="_method" value="DELETE"/>
        </form>
    
    
    </body>
    
    // add.jsp (添加之前的编辑页面)
    
    // 需要引入 springframework 的标签
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
    
    <body>
    <h2>添加客户</h2>
    <form:form action="${pageContext.request.contextPath}/emp" method="post"
                                                                modelAttribute="employee">
        lastName:<form:input path="lastName"/><br/>
        email:<form:input path="email"/><br/>
        gender:<form:radiobuttons path="gender" items="${requestScope.genders}"/><br/>
        department:<form:select path="department.id" items="${requestScope.departments}"
                        itemLabel="departmentName" itemValue="id"/></form:select><br/>
        <input type="submit" value="保存"/>
    </form:form>
    </body>
    
    // edit.jsp (修改页面)
    <h2>修改客户</h2>
    <form:form action="${pageContext.request.contextPath}/emp" method="post"
                                                                modelAttribute="employee">
        <input type="hidden" name="_method" value="PUT"/><br/>
        <form:hidden path="id"/>
    
        email:<form:input path="email"/><br/>
        gender:<form:radiobuttons path="gender" items="${requestScope.genders}"/><br/>
        department:<form:select path="department.id" items="${requestScope.departments}"
                        itemLabel="departmentName" itemValue="id"/></form:select><br/>
        <input type="submit" value="修改"/>
    </form:form>
    </body>
    
    
    // springDispatcherServlet-servlet.xml 配置文件
    // 若将 web.xml 中 的 DispatcherServlet 请求映射配置为 "/", 则 SpringMVC 将捕获 Web 容器
    // 的所有请求,包括静态资源的请求, SpringMVC 会将它们当成一个普通请求处理,会找不到对应处理器;
    // 可以在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>,以解决静态资源的问题:
    //    * <mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个
    //    * DefaultServletHttpRequestHandler, 它会对 DispatcherServlet 的请求进行筛查,
    //    * 如果发现是没有经过映射的请求,就将该请求交由WEB应用服务器默认的 Servlet 处理,如果不是
    //    * 静态资源的请求,才由 DispatcherServlet 继续处理;
    //    一般 WEB 应用服务器默认的 Servlet 的名称都是 default, 若所使用的 WEB 服务器的默认
    //    Servlet 名称不是 default, 则需要通过 default-servlet-name 属性显示指定;
    
    <!-- 处理静态资源导入, 例如导入 jquery -->
    <mvc:default-servlet-handler/>
    <!-- 如果只有 mvc:default-servlet-handler, 注解类失效, 还需要配置 annotation-driven -->
    <mvc:annotation-driven></mvc:annotation-driven>
    
    // web.xml
    // 默认情况下,PUT和DELETE请求是无法提交表单数据的;
    // 解决方案: 在web.xml中配置spring提供的过滤器解决或者在页面中使用form表单设置
    <filter>
        <filter-name>HttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--
          将POST请求转化为DELETE或者是PUT
           要用_method指定真正的请求参数
    -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    
    // EmployeeHandler.java
    
    @Controller
    public class EmployeeHander{
    
        @Autowired
        private EmployeeService employeeService;
        @Autowired
        private DepartmentService departmentService;
    
        // 跳转进入添加员工信息页面
        // 需要查询出性别,全部部门
        @RequestMapping(value="/emp",method=RequestMethod.GET)
        public String addPre(Map<String,Object> map){
    
            // 1. 查询出全部部门
            map.put("departments",departmentService.getDepartments());
            // 2. 查出性别
            map.put("genders",getGenderUtils());
    
            // 3. 新建承载的 bean, 实现与前台 form 表单的对应
    
            // 在进入 spring form 标签设定 binding 对象页面前,必须有一个 binding 的对象
            // 放在 context 中,spring 才能 binding.类似struts的context statck和value stack.
    
            // 可以在 form 表单上通过 modelAttribute 属性指定绑定的模型属性,
            // 如果没有指定该属性, 则默认从 request 域对象中读取 command 的表单 bean.
            // 如果bean不存在,则会引发异常.
    
            map.put("employee", new Employee());
    
            return "add";
        }
    
        // 保存员工信息
        @RequestMapping(value="/emp",method=RequestMethod.POST)
        public String add(Employee employee){
            employeeService.add(employee);
            return "redirect:/emps";
        }
    
        // 删除员工信息
        @RequestMapping(value="/emp/{id}",method=RequestMethod.DELETE)
        public String delete(@PathVariable("id") Integer id){
            employeeService.delete(id);
            return "redirect:/emps";
        }
    
        // 修改之前,跳转到修改页面
        // lastName 不可修改
        @RequestMapping(value="/emp/{id}",method=RequestMethod.GET)
        public String editPre(@PathVariable("id") Integer id, Map<String,Object> map){
            map.put("departments",departmentService.getDepartments());
            map.put("genders",getGenderUtils());
            map.put(employee,employeeService.get(id));
            return "edit";
        }
    
        // 修改
        @RequestMapping(value="/emp",method=RequestMethod.PUT)
        public String update(Employee employee){
    
            employeeService.save(employee);
            return "redirect:/emps";
        }
    
        // 保证 lastName 不能修改,且值不变
        @ModelAttribute
        public void getEmployeeById(@RequestParam(value="id",required=false) Integer id,
                                                Map<String,Object> map){
            if(null != id){
                map.put("employee",employeeService.get(id));
            }
        }
    
    
        // 查询所有员工
        @RequestMapping(value="/emps",method=RequestMethod.GET)
        public String list(Map<String,Object> map){
    
            map.put("employees",employeeService.findAll());
            return "list";
        }
    
        // 模拟从 LOV(list of value) 数据库查询出 gender 的值
        private Map<String, String> getGenderUtils(){
            Map<String,String> genders = new HashMap<String,String>();
    
            genders.put("1","male");
            genders.put("0","female");
    
            return genders;
        }
    }
    

    参考资料

  • 相关阅读:
    初步掌握Yarn的架构及原理(转)
    CORS 专题
    WebSocket是一种协议
    InputStream中read()与read(byte[] b)(转)
    listview异步加载sd卡图片
    ListView getView中放置多个item和getItemViewType的用法
    Android ListView异步加载数据
    android sqlite 一次创建多个表
    Android Adapter的getViewTypeCount和getItemViewType
    Android 获取SDCard上图片和视频的缩略图
  • 原文地址:https://www.cnblogs.com/linkworld/p/7777019.html
Copyright © 2020-2023  润新知