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;
    }
}


參考資料javascript

相關文章
相關標籤/搜索