2015年7月16日 星期四

Spring MVC中Controller與View的傳值

Spring MVC中很有趣的地方就是Controller可以為一個POJO或是一個普通的Java Bean,不用繼承如HttpServlet等類別,而要將請求導向View(例如JSP)時,也只要回傳String或ModeAndView等,不用使用像response.sendRedirect()或request.getRequestDispatcher("XXX").forward(request, response)等語法,使得單元測試變得更為簡單與容易。

那麼View和Controller之間要怎麼溝通呢?在這裡以一個簡單的例子實作來展示一些在Spring中View和Controller之間溝通的方式。

例子要實做的內容為,使用者一開始會進入一個表單輸入頁面,可以輸入名字及選擇稱謂(先生或小姐),按下送出按鈕後,會引導到招呼畫面,其中招呼內容包含輸入的名字及稱謂,而如果名字為雨果及稱謂為先生的話,招呼畫面的內容會稍有不同。

首先先來看專案的配置:



其中包括:

  1. dispatcher-servlet.xml:Spring的dispatcherServlet設定檔。
  2. FormView.html:一開始的表單輸入頁面。
  3. FormCheckResultView.jsp:招呼畫面。
  4. FormController.java:接收及轉送參數的Controller。
在web.xml中,我們讓所有的url請求都先讓Spring處理:


web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

這個例子的Controller主要以注解(annotation)的方式實作,所以不要忘了在Spring的設定檔中(例如dispatcher-servlet.xml)加入支援Annotation的語法,以下為Spring設定檔中的內容。

dispatcher-servlet.xml:

<?xml version='1.0' encoding='UTF-8' ?>
<!-- was: <?xml version="1.0" encoding="UTF-8"?> -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <mvc:resources location="/" mapping="/**"/>            
    <context:component-scan base-package="/Controller" />  <!-- 掃描package中有用注解的類別 -->
    <mvc:annotation-driven />                              <!-- 使用注解驅動 -->
    
</beans>


在FormView.html中,只是簡單的設置了表單、選項及按鈕:

FormView.html:

<!DOCTYPE html>
<html>
    <head>
        <title>填表輸入頁面</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <div>填表輸入頁面</div>
        <form action="FormController.do">
            輸入名字:<input type="text" name="name" /> <br/>
            先生或小姐:
            <select name="sex">
                <option value="先生">先生</option>
                <option value="小姐">小姐</option>
            </select> 
            <br/>
            <input type="submit" value="確定送出" />
        </form>
    </body>
</html>


主要的重頭戲在FormController.java中,可以看到如果忽略Spring的Annotation的話,就只是一個普通的POJO而已,其中可以看到所展示的兩種從FormView.html中得到表單參數、及將參數送往FormResultCheckView.jsp的方式,還有如何判斷參數狀況來使用不同的Method處理的方式:

FormController.java:

package Controller;

import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class FormController {

    //一開始無任何參數,直接返回表單輸入頁面
    @RequestMapping(value = {"/", "FormController.do"})
    public String returnToFormView() {
        return "/FormView.html";
    }

    //第一種Controller傳參數給View的方式,
    //使用回傳ModelAndView及ModelAndView.addObject()放參數進去給View
    //如果有名為name的參數,執行toResultViewForNormal()
    @RequestMapping(value = "FormController.do", params = {"name"})
    public ModelAndView toResultViewForNormal(@RequestParam("name") String name, @RequestParam("sex") String sex) {
        ModelAndView modelAndView = new ModelAndView("/FormCheckResultView.jsp");
        modelAndView.addObject("name", name);
        modelAndView.addObject("sex", sex);
        return modelAndView;
    }
    
    //第二種Controller傳參數給View的方式,
    //使用回傳String及map.out()放參數進去給View
    //如果有名為name及sex的參數,並且name的值為"雨果"、sex的值為"先生",則執行checkAndToResultView()
    @RequestMapping(value = "FormController.do", params = {"name=雨果","sex=先生"})
    public String toResultViewForGreatHugo(@RequestParam("name") String name, @RequestParam("sex") String sex, Map<String, Object> map) {
        map.put("name", name);
        map.put("sex", sex);
        map.put("greeting", "偉大的");    //多傳一個"greeting"參數給View
        return "/FormCheckResultView.jsp";
    }

}

最後在FormCheckResultView.jsp中,實現了一個簡單的依傳進參數來顯示的招呼畫面:

FormCheckResultView.jsp:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>歡迎頁面</title>
    </head>
    <body>
        <h1>您好, ${greeting} ${name} ${sex}
    </body>
</html>

成品如下,如果輸入了名字並選擇先生或小姐的稱謂時,會被引導至招呼畫面:


而如果輸入的名字是 "雨果" 且稱謂為 "先生" 時,招呼畫面的顯示會有所不同:


附上原始碼:
SpringViewContollerCommunication.7z

參考資料:
  1. Spring MVC and Thymeleaf: how to access data from templates
  2. SpringMVC常用註解實例詳解2:@ModelAttribute GOOD
  3. Spring 教學(2) - 關於@RequestMapping


4 則留言 :

  1. 您好

    照您上面的方法做,我這邊出現這個錯誤,
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcher' processing GET request for [/SpringSample/FormController.do]
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /FormController.do
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [/FormController.do]
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - Matching patterns for request [/FormController.do] are [/**]
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - URI Template variables for request [/FormController.do] are {}
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - Mapping [/FormController.do] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@193949b]]] and 1 interceptor
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - Last-Modified value for [/SpringSample/FormController.do] is: -1
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
    18:15:32.439 [http-nio-8080-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request

    可以請教您如何解決嗎?

    回覆刪除
    回覆
    1. 您可以下載我的原始碼,解壓縮後用Netbeans開Project測試看看,應該是可以成功的,可以跟你的專案比較看看。
      因為沒看到你的完整專案不能確定,不過我看了你的錯誤訊息,猜測應該是url-pattern沒有設好或目錄結構跟我不同等,造成Srping的dispatcher servlet找不到你的url路徑(/SpringSample/FormController.do)應該要對應的FormController.class中的method。記得註意檔案放置的位置及xml配置檔的內容

      刪除
  2. 請問您的web.xml,為什麼有這個錯誤:cvc-complex-type.2.3: Element 'context-param' cannot have character [children], because the type's content type is element-only.可是整個專案還是可以執行?? 可以請教你為什麼會這樣嗎???

    回覆刪除
    回覆
    1. 我沒有看到您所說的錯誤訊息,我猜測您是否是使用Eclipse?或是直接複製貼上我網頁上web.xml的內容?如果是的話建意可先貼到記事本上,再貼到您要的地方,以避免複製了一些xml不合法的字符。或是您可以使用包住有xml不合法字符的內容。或是檢查xml內容,是否有不正確的字符、空白等內容。

      刪除