FreeMarker

Apache FreeMarker 是一個樣板引擎,用於從 HTML 到電子郵件等任何種類的文字輸出。Spring Framework 內建了將 Spring MVC 與 FreeMarker 樣板整合的功能。

檢視組態

以下範例展示如何將 FreeMarker 組態為檢視技術

  • Java

  • Kotlin

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.freeMarker();
	}

	// Configure FreeMarker...

	@Bean
	public FreeMarkerConfigurer freeMarkerConfigurer() {
		FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
		configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
		configurer.setDefaultCharset(StandardCharsets.UTF_8);
		return configurer;
	}
}
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {

	override fun configureViewResolvers(registry: ViewResolverRegistry) {
		registry.freeMarker()
	}

	// Configure FreeMarker...

	@Bean
	fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
		setTemplateLoaderPath("/WEB-INF/freemarker")
		setDefaultCharset(StandardCharsets.UTF_8)
	}
}

以下範例展示如何在 XML 中組態相同的內容

<mvc:annotation-driven/>

<mvc:view-resolvers>
	<mvc:freemarker/>
</mvc:view-resolvers>

<!-- Configure FreeMarker... -->
<mvc:freemarker-configurer>
	<mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>

或者,您也可以宣告 FreeMarkerConfigurer Bean 以完全控制所有屬性,如下列範例所示

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
	<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
	<property name="defaultEncoding" value="UTF-8"/>
</bean>

您的樣板需要儲存在上述範例中 FreeMarkerConfigurer 指定的目錄中。以上述組態為例,如果您的控制器傳回檢視名稱 welcome,則解析器會尋找 /WEB-INF/freemarker/welcome.ftl 樣板。

FreeMarker 組態

您可以將 FreeMarker 的「Settings」和「SharedVariables」直接傳遞到 FreeMarker Configuration 物件(由 Spring 管理),方法是在 FreeMarkerConfigurer Bean 上設定適當的 Bean 屬性。freemarkerSettings 屬性需要 java.util.Properties 物件,而 freemarkerVariables 屬性需要 java.util.Map。以下範例展示如何使用 FreeMarkerConfigurer

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
	<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
	<property name="freemarkerVariables">
		<map>
			<entry key="xml_escape" value-ref="fmXmlEscape"/>
		</map>
	</property>
</bean>

<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>

如需套用至 Configuration 物件的設定和變數詳細資訊,請參閱 FreeMarker 文件。

表單處理

Spring 提供了一個標籤庫,用於 JSP,其中包含 <spring:bind/> 元素等等。此元素主要讓表單顯示來自表單後端物件的值,並顯示來自 Web 或業務層中 Validator 的失敗驗證結果。Spring 也支援 FreeMarker 中相同的功能,並提供額外的便利巨集來產生表單輸入元素本身。

繫結巨集

標準巨集集維護在 FreeMarker 的 spring-webmvc.jar 檔案中,因此它們始終可用於適當組態的應用程式。

Spring 樣板庫中定義的某些巨集被視為內部(私有),但巨集定義中不存在此類作用域,使得所有巨集都對呼叫程式碼和使用者樣板可見。以下章節僅著重於您需要直接從樣板內呼叫的巨集。如果您希望直接檢視巨集程式碼,檔案名為 spring.ftl,位於 org.springframework.web.servlet.view.freemarker 套件中。

簡單繫結

在基於 FreeMarker 樣板的 HTML 表單中,這些樣板充當 Spring MVC 控制器的表單檢視,您可以使用類似於下一個範例的程式碼來繫結到欄位值,並以類似於 JSP 對等項目的方式顯示每個輸入欄位的錯誤訊息。以下範例展示了 personForm 檢視

<!-- FreeMarker macros have to be imported into a namespace.
	We strongly recommend sticking to 'spring'. -->
<#import "/spring.ftl" as spring/>
<html>
	...
	<form action="" method="POST">
		Name:
		<@spring.bind "personForm.name"/>
		<input type="text"
			name="${spring.status.expression}"
			value="${spring.status.value?html}"/><br />
		<#list spring.status.errorMessages as error> <b>${error}</b> <br /> </#list>
		<br />
		...
		<input type="submit" value="submit"/>
	</form>
	...
</html>

<@spring.bind> 需要 'path' 引數,該引數包含您的命令物件的名稱(除非您在控制器組態中變更了它,否則為 'command'),後跟句點以及您希望繫結到的命令物件上的欄位名稱。您也可以使用巢狀欄位,例如 command.address.streetbind 巨集假定 web.xmlServletContext 參數 defaultHtmlEscape 指定的預設 HTML 逸出行為。

巨集的替代形式稱為 <@spring.bindEscaped>,它接受第二個引數,明確指定是否應在狀態錯誤訊息或值中使用 HTML 逸出。您可以根據需要將其設定為 truefalse。其他表單處理巨集簡化了 HTML 逸出的使用,您應盡可能使用這些巨集。這些巨集將在下一節中說明。

輸入巨集

FreeMarker 的其他便利巨集簡化了繫結和表單產生(包括驗證錯誤顯示)。永遠不需要使用這些巨集來產生表單輸入欄位,您可以將它們與簡單的 HTML 或先前重點介紹的 Spring 繫結巨集的直接呼叫混合搭配使用。

下表顯示了可用巨集的 FreeMarker 樣板 (FTL) 定義以及每個巨集採用的參數清單

表 1. 巨集定義表
巨集 FTL 定義

message(根據代碼參數從資源套件輸出字串)

<@spring.message code/>

messageText(根據代碼參數從資源套件輸出字串,回退到預設參數的值)

<@spring.messageText code, text/>

url(在相對 URL 前面加上應用程式的 Context 根目錄)

<@spring.url relativeUrl/>

formInput(用於收集使用者輸入的標準輸入欄位)

<@spring.formInput path, attributes, fieldType/>

formHiddenInput(用於提交非使用者輸入的隱藏輸入欄位)

<@spring.formHiddenInput path, attributes/>

formPasswordInput(用於收集密碼的標準輸入欄位。請注意,此類型欄位中永遠不會填入值。)

<@spring.formPasswordInput path, attributes/>

formTextarea(用於收集長型自由格式文字輸入的大型文字欄位)

<@spring.formTextarea path, attributes/>

formSingleSelect(下拉式選項方塊,可讓選取單個必要值)

<@spring.formSingleSelect path, options, attributes/>

formMultiSelect(選項清單方塊,可讓使用者選取 0 個或多個值)

<@spring.formMultiSelect path, options, attributes/>

formRadioButtons(一組單選按鈕,可讓從可用選項中進行單選)

<@spring.formRadioButtons path, options separator, attributes/>

formCheckboxes(一組核取方塊,可讓選取 0 個或多個值)

<@spring.formCheckboxes path, options, separator, attributes/>

formCheckbox(單個核取方塊)

<@spring.formCheckbox path, attributes/>

showErrors(簡化繫結欄位的驗證錯誤顯示)

<@spring.showErrors separator, classOrStyle/>

在 FreeMarker 樣板中,實際上並不需要 formHiddenInputformPasswordInput,因為您可以使用一般的 formInput 巨集,將 hiddenpassword 指定為 fieldType 參數的值。

上述任何巨集的參數都具有一致的含義

  • path:要繫結的欄位名稱(例如,「command.name」)

  • optionsMap,其中包含輸入欄位中可選取的所有可用值。Map 的鍵表示從表單 POST 回並繫結到命令物件的值。針對鍵儲存的 Map 物件是在表單上向使用者顯示的標籤,並且可能與表單 POST 回的對應值不同。通常,此類 Map 由控制器作為參考資料提供。您可以根據所需的行為使用任何 Map 實作。對於嚴格排序的 Map,您可以使用具有適當 ComparatorSortedMap(例如 TreeMap),對於應按插入順序傳回值的任意 Map,請使用 LinkedHashMap 或來自 commons-collectionsLinkedMap

  • separator:在多個選項作為離散元素(單選按鈕或核取方塊)可用的情況下,用於分隔清單中每個選項的字元序列(例如 <br>)。

  • attributes:要包含在 HTML 標籤本身內的任意標籤或文字的額外字串。此字串由巨集逐字回顯。例如,在 textarea 欄位中,您可以提供屬性(例如 'rows="5" cols="60"'),或者您可以傳遞樣式資訊,例如 'style="border:1px solid silver"'。

  • classOrStyle:對於 showErrors 巨集,包裝每個錯誤的 span 元素使用的 CSS 類別名稱。如果未提供任何資訊(或值為空),則錯誤會包裝在 <b></b> 標籤中。

以下章節概述了巨集的範例。

輸入欄位

formInput 巨集採用 path 參數 (command.name) 和額外的 attributes 參數(在即將到來的範例中為空)。此巨集與所有其他表單產生巨集一起,對路徑參數執行隱式 Spring 繫結。繫結保持有效,直到發生新的繫結,因此 showErrors 巨集不需要再次傳遞路徑參數 — 它對上次建立繫結的欄位進行操作。

showErrors 巨集採用分隔符號參數(用於分隔給定欄位上多個錯誤的字元),並且還接受第二個參數 — 這次是類別名稱或樣式屬性。請注意,FreeMarker 可以為 attributes 參數指定預設值。以下範例展示如何使用 formInputshowErrors 巨集

<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>

下一個範例展示了表單片段的輸出,產生名稱欄位並在表單提交時欄位中沒有值後顯示驗證錯誤。驗證透過 Spring 的驗證框架進行。

產生的 HTML 類似於以下範例

Name:
<input type="text" name="name" value="">
<br>
	<b>required</b>
<br>
<br>

formTextarea 巨集的工作方式與 formInput 巨集相同,並接受相同的參數清單。通常,第二個參數 (attributes) 用於傳遞樣式資訊或 textarearowscols 屬性。

選取欄位

您可以使用四個選取欄位巨集,在 HTML 表單中產生常見的 UI 值選取輸入

  • formSingleSelect

  • formMultiSelect

  • formRadioButtons

  • formCheckboxes

四個巨集中的每一個都接受選項的 Map,其中包含表單欄位的值和對應於該值的標籤。值和標籤可以相同。

下一個範例適用於 FTL 中的單選按鈕。表單後端物件為此欄位指定了預設值 'London',因此不需要驗證。當表單呈現時,要從中選擇的整個城市清單作為參考資料在模型中以名稱 'cityMap' 提供。以下清單顯示了範例

...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>

上述清單呈現了一行單選按鈕,每個按鈕對應 cityMap 中的每個值,並使用分隔符號 ""。未提供其他屬性(巨集的最後一個參數遺失)。cityMap 對於 Map 中的每個鍵值對使用相同的 String。Map 的鍵是表單實際提交為 POST 請求參數的內容。Map 值是使用者看到的標籤。在上述範例中,假設表單後端物件中提供了三個著名的城市清單和預設值,則 HTML 類似於以下內容

Town:
<input type="radio" name="address.town" value="London">London</input>
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
<input type="radio" name="address.town" value="New York">New York</input>

如果您的應用程式希望透過內部代碼(例如)處理城市,則可以使用適當的鍵建立代碼 Map,如下列範例所示

  • Java

  • Kotlin

protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
	Map<String, String> cityMap = new LinkedHashMap<>();
	cityMap.put("LDN", "London");
	cityMap.put("PRS", "Paris");
	cityMap.put("NYC", "New York");

	Map<String, Object> model = new HashMap<>();
	model.put("cityMap", cityMap);
	return model;
}
protected fun referenceData(request: HttpServletRequest): Map<String, *> {
	val cityMap = linkedMapOf(
			"LDN" to "London",
			"PRS" to "Paris",
			"NYC" to "New York"
	)
	return hashMapOf("cityMap" to cityMap)
}

程式碼現在產生輸出,其中單選按鈕值是相關代碼,但使用者仍然看到更使用者友善的城市名稱,如下所示

Town:
<input type="radio" name="address.town" value="LDN">London</input>
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
<input type="radio" name="address.town" value="NYC">New York</input>

HTML 逸出

預設情況下,如先前所述表單巨集的使用會產生符合 HTML 4.01 標準的 HTML 元素,並使用您 web.xml 檔案中定義的預設 HTML 跳脫值,如同 Spring 的綁定支援所使用的方式。若要讓元素符合 XHTML 標準,或覆寫預設的 HTML 跳脫值,您可以在範本(或模型,範本可見之處)中指定兩個變數。在範本中指定它們的優點是,它們可以在範本處理的後續階段變更為不同的值,以便為表單中的不同欄位提供不同的行為。

若要切換為標籤的 XHTML 標準相容性,請為名為 xhtmlCompliant 的模型或上下文變數指定值 true,如下列範例所示

<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>

在處理此指示後,Spring 巨集產生的任何元素現在都符合 XHTML 標準。

以類似的方式,您可以針對每個欄位指定 HTML 跳脫,如下列範例所示

<#-- until this point, default HTML escaping is used -->

<#assign htmlEscape = true>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name"/>

<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML escaping off -->