- Spring resolver
- 將controller中請求處理的logic和view中render的implement decouple是Spring MVC的特性
- 如果controller method直接產生HTML,很難在不影響request處理logic的前提下維護更新view
- controller和view會在model的內容達成一致,這是最大關聯。除此之外,兩者應該保持距離
- 而Spring resolver的重點功能就在於透過logic view name知道要使用哪過view render model
- Spring MVE ViewResolver interface:
- 給定resolveViewName後會回傳一個View instance
- View是另外一個interface,為了接受model及request&response,並將結果輸出到response
- Spring提供了一些13個out-of-the-box implementations,能夠適應大多情境
- BeanNameViewResolver
- ContentNegotiatingViewResolver
- FreeMarkerViewResolver: FreeMarker
- InternalResourceViewResolver: JSP
- JasperReportsViewResolver
- ResourceBundleViewResolver
- TilesViewResolver: Apache Tiles view
- UrlBasedViewResolver
- VelocityLayoutViewResolver
- VelocityViewResolver: Velocity
- XmlViewResolver
- XsltViewResolver
- Create JSP view
- Spring提供兩個support JSP view的方式
- InternalResourceViewResolver會將view名稱解析為JSP,且能夠將view name解析為jstlView型式的JSP文件,將JSTL localize及resource bundle給JSTL formatting及message label
- Spring提供兩個JSP tag library,一個用在form到model的bundle,另一個提供通用的utility features
- InternalResourceViewResolver將logic name對應為真實的physical path的方式是加上prefix及suffix
- 例如如果將JSP都放在WEB-INF folder下,防止對他的直接access,則假設在WEB-INF/views/有個home.jsp,那麼logic view name: home,則能夠因為下面的設定:
- prefix:WEB-INF/views/
- suffix: jsp
- 組出WEB-INF/views/home.jsp
- 這樣的話,就可以很方便地將view model組織為目錄結構,而不是都放在固定目錄下
- 使用@Bean annotation時,可以如下範例配置InternalResourceViewResolver
- 也可以使用XML
- JSTL view
- 如果JSP使用JSTL tag來處理formatting及message的時候,會希望InternalResourceViewResolver能夠將view解析為JstlView
- JSTL formatting label需要一個Locale object(設定data, currenct使用)
- 將view解析為JstlView,而不是InternalResourceView,只需要設定viewClass property
- XML:
- 使用Spring的JSP library
- Binding forms to the model
- 宣告:
- 此處的prefix為sf,通常也會使用form prefix
- 共14種tag,可render在HTML form上
- <sf:checkbox>
- <sf:checkboxes>
- <sf:errors>
- <sf:form>
- <sf:hidden>
- <sf:input>
- <sf:label>
- <sf:option>
- <sf:options>
- <sf:password>
- <sf:radiobutton>
- <sf:radiobuttons>
- <sf:select>
- <sf:textarea>
- 使用範例:
- 也能夠設定context在model object(此處是 commandName,property是spitter)
- 在其他form-binding tags會引用這個model object的property
- 注意此處將commandName property設定為spitter,這表示在導往registerForm的model中必須要有一個spitter key,否則form將會有問題。
- 因此:原本的controller處理registration:
- 應修改為
- 最後的html其實會像這樣:
- 另外,Spring3.1後,<sf:input>能夠指定type property,除了可以選的type外,還能指定HTML5特定type,如date、range、email。如下:
public interface ViewResolver { View resolveViewName(String viewName, Locale locale) throws Exception; }
public interface View { String getContentType(); void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception; }
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); return resolver; }
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp" />
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); return resolver; }
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp" p:viewClass="org.springframework.web.servlet.view.JstlView" />
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
<sf:form method="POST" commandName="spitter"> First Name: <sf:input path="firstName" /><br/>// type將會設定為text Last Name: <sf:input path="lastName" /><br/> // path值就等同於name值 Email: <sf:input path="email" /><br/> Username: <sf:input path="username" /><br/> Password: <sf:password path="password" /><br/> //不會顯示明文 <input type="submit" value="Register" /> </sf:form>
@RequestMapping(value = "/register", method=GET) public String showRegistrationForm() { return "registerForm"; }
@RequestMapping(value="/register", method=GET) public String showRegistrationForm(Model model) { model.addAttribute(new Spitter()); return "registerForm"; }
<form id="spitter" action="/spitter/spitter/register" method="POST"> First Name: <input id="firstName" name="firstName" type="text" value="J" /> <br/> Last Name: <input id="lastName" name="lastName" type="text" value="B" /> <br/> Email: <input id="email" name="email" type="text" value="jack" /> <br/> Username: <input id="username" name="username" type="text" value="jack" /> <br/> Password: <input id="password" name="password" type="password" value="" /> <br/> <input type="submit" value="Register" /> </form>
Email: <sf:input path="email" type="email" /><br/>
- 其實渲染在HTML就是
- Display error
- 如果存在validation error,request中會包含錯誤的訊息,這些message會和model放在一起,需要做的事情就是將data從model中取出
- 使用<sf:errors>能夠讓任務變得簡單,可以也設定cssClass="error",使其明顯(需設定css color:red;)
- 渲染出的HTML如下:
- 這種方法好的地方在於很明顯,使用者可以知道問題在哪,但有可能會造成排版有問題
- 解決:放在同一個地方進行顯示
- 移除每個input上的<sf:errors> elements,並將其放到form的頂部
- path="*" 表示display所有attribute的錯誤
- element="div"
- 默認是span,只顯示一個錯誤還可以
- 整個input field的錯誤,可能不只一個錯誤,最好使用div區塊元素
- 設定css:(紅邊匡,淺紅背景)
- 設定需要修正的input field,可以設定<sf:label>並新增cssErrorClass
- 若沒有問題的話,則render的HTML如下
- 有error的話,Label class會被設定為error
- 除此之外可以設定error css:
- 在Spitter class中,可以再validation annotation上設定message property。可以引用更friendly的error message。message能夠定義在property document中
- 注意使用的是大括號,指的是property
- 新增一ValidationMessages.properties在root classpath下
- key值對應在annotation中的property placeholder的value
- 沒有hardcode
- 也能夠在創建一個ValidationMessages.properties是針對其他國語言的
- Spring's general tag library
- Spring通用的tag library,需要再page上進行declare(prefix可自訂)
- 可使用以下10種JSP tag
<s:bind>: form相關,已被淘汰- <s:escapeBody>
- <s:hasBindErrors>
- <s:htmlEscape>
- <s:message>: internationalization
- <s:nestedPath>
- <s:theme>
- <s:transform>
- <s:url>
- <s:eval>
- <s:message>
- 例如今天要將<h1>Welcome to spittr!</h1>換成國際化,則可以使用之
- 上例,會根據key為spitter.welcome的message resource render text
- Spring有多個message resource class,都implement MessageSource interface,其中比較常見及有用的是ResourceBundleMessageSource
- 會試著在root path的property document中解析message
- 另外一個方式是使用ReloadableResourceBundleMessageSource,差別在於此class能夠重新reload而不用重新編譯或啟動application
- Basename是從外部找,而非從classpath下找
- prefix: classpath: 從classpath下找
- prefix: file: 指定file下找
- prefix: 沒有,代表從web application的root path下開始找
- 建立property
- default名字為message.properties,內容可設定如下
- default名字為message.properties,內容可設定如下
- spittr.welcome=Welcome to Spittr!
- 西班牙文可設定property名字為message_es.properties,內容可設定如下
- spittr.welcome=Bienvenidos a Spittr!
- 建立URL˙
- <s:url>建立一個相對於Servlet context的URL
- 假設Servlet context為"spittr",則會render html如下
- 也可以變成一個變數,之後在template中使用,
- default scope是page,透過scope property,可以讓<s:url>在session, application或request中使用
- 也可以設定parameters
- 亦可以使用在path中有parameter的URL,這個變數會參考到下面的<s:param>指定的parameter
- 若使用<s:url>不想使用到hyperlink,想要讓他直接呈現在畫面上,則可以使用htmlEscape
- 則render出的HTML: /spitter/spittles?max=60&count=20
- 如果希望在Javascript中使用URL,則可將javascriptEscpae設定為true
- 則結果為 var spittlesUrl = "\/spitter\/spittles?max=60&count=20"
- Escaping content (轉譯內容)
- 以上結果為<h1>Hello</h1>
- 也support javascript轉譯,但不能設定為variable
Email: <input id="email" name="email" type="email" value="jack"/><br/>
<sf:form method="POST" commandName="spitter"> First Name: <sf:input path="firstName" /> <sf:errors path="firstName" cssClass="error"/><br/> ... </sf:form>
First Name: <input id="firstName" name="firstName" type="text" value="J"/> <span id="firstName.errors">size must be between 2 and 30</span>
<sf:form method="POST" commandName="spitter" > <sf:errors path="*" element="div" cssClass="errors" /> ... </sf:form>
div.errors { background-color: #ffcccc; border: 2px solid red; }
<sf:form method="POST" commandName="spitter" > <sf:label path="firstName" cssErrorClass="error">First Name</sf:label>: <sf:input path="firstName" cssErrorClass="error" /><br/> ... </sf:form>
<label for="firstName">First Name</label>
<label for="firstName" class="error">First Name</label>
label.error { color: red; } input.error { background-color: #ffcccc; }
@NotNull @Size(min=5, max=16, message="{username.size}") private String username; @NotNull @Size(min=5, max=25, message="{password.size}") private String password; @NotNull @Size(min=2, max=30, message="{firstName.size}") private String firstName; @NotNull @Size(min=2, max=30, message="{lastName.size}") private String lastName; @NotNull @Email(message="{email.valid}") private String email;
firstName.size= First name must be between {min} and {max} characters long. lastName.size= Last name must be between {min} and {max} characters long. username.size= Username must be between {min} and {max} characters long. password.size= Password must be between {min} and {max} characters long. email.valid=The email address must be valid.
<%@ taglib uri="http://www.springframework.org/tags" prefix="s"%>
<h1><s:message code="spittr.welcome" /></h1>
@Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("messages"); // 設定property doc return messageSource; }
@Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("file:///etc/spittr/messages"); messageSource.setCacheSeconds(10); return messageSource; }
<a href="<s:url href="/spitter/register" />">Register</a>
<a href="/spittr/spitter/register">Register</a>
<s:url href="/spitter/register" var="registerUrl" scope="request"/>
<a href="${registerUrl}">Register</a>
<s:url href="/spittles" var="spittlesUrl"> <s:param name="max" value="60" /> <s:param name="count" value="20" /> </s:url>
<s:url href="/spitter/{username}" var="spitterUrl"> <s:param name="username" value="jbauer" /> </s:url>
<s:url value="/spittles" htmlEscape="true"> <s:param name="max" value="60" /> <s:param name="count" value="20" /> </s:url>
<s:url value="/spittles" var="spittlesJSUrl" javaScriptEscape="true"> <s:param name="max" value="60" /> <s:param name="count" value="20" /> </s:url> <script> var spittlesUrl = "${spittlesJSUrl}" </script>
<s:escapeBody htmlEscape="true"> <h1>Hello</h1> </s:escapeBody>
<s:escapeBody javaScriptEscape="true"> <h1>Hello</h1> </s:escapeBody>
沒有留言:
張貼留言